Delay conversion of v8 exceptions to QQmlErrors.
[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 = true;
224
225     int id = script.d.data()->bindingId;
226     if (id >= 0) {
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);
231             Q_ASSERT(typeData);
232
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);
237             }
238
239             typeData->release();
240         }
241     }
242
243     if (defaultConstruction)
244         d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject());
245 }
246
247 /*!
248     Create a QQmlExpression object that is a child of \a parent.
249
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.
253 */
254 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
255                                                QObject *scope,
256                                                const QString &expression,
257                                                QObject *parent)
258 : QObject(*new QQmlExpressionPrivate, parent)
259 {
260     Q_D(QQmlExpression);
261     d->init(QQmlContextData::get(ctxt), expression, scope);
262 }
263
264 /*!
265     \internal
266 */
267 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
268                                                const QString &expression)
269 : QObject(*new QQmlExpressionPrivate, 0)
270 {
271     Q_D(QQmlExpression);
272     d->init(ctxt, expression, scope);
273 }
274
275 /*!  \internal */
276 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
277                                                const QString &expression, QQmlExpressionPrivate &dd)
278 : QObject(dd, 0)
279 {
280     Q_D(QQmlExpression);
281     d->init(ctxt, expression, scope);
282 }
283
284 /*!
285     Destroy the QQmlExpression instance.
286 */
287 QQmlExpression::~QQmlExpression()
288 {
289 }
290
291 /*!
292     Returns the QQmlEngine this expression is associated with, or 0 if there
293     is no association or the QQmlEngine has been destroyed.
294 */
295 QQmlEngine *QQmlExpression::engine() const
296 {
297     Q_D(const QQmlExpression);
298     return d->context()?d->context()->engine:0;
299 }
300
301 /*!
302     Returns the QQmlContext this expression is associated with, or 0 if there
303     is no association or the QQmlContext has been destroyed.
304 */
305 QQmlContext *QQmlExpression::context() const
306 {
307     Q_D(const QQmlExpression);
308     QQmlContextData *data = d->context();
309     return data?data->asQQmlContext():0;
310 }
311
312 /*!
313     Returns the expression string.
314 */
315 QString QQmlExpression::expression() const
316 {
317     Q_D(const QQmlExpression);
318     if (!d->expressionUtf8.isEmpty()) {
319         return QString::fromUtf8(d->expressionUtf8);
320     } else {
321         return d->expression;
322     }
323 }
324
325 /*!
326     Set the expression to \a expression.
327 */
328 void QQmlExpression::setExpression(const QString &expression)
329 {
330     Q_D(QQmlExpression);
331
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);
339 }
340
341 // Must be called with a valid handle scope
342 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined)
343 {
344     if (!expressionFunctionValid) {
345         bool ok = true;
346
347         QQmlRewrite::RewriteBinding rewriteBinding;
348         rewriteBinding.setName(name);
349         QString code;
350         if (expressionFunctionRewritten)
351             code = expression;
352         else
353             code = rewriteBinding(expression, &ok);
354
355         if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
356         setUseSharedContext(false);
357         expressionFunctionValid = true;
358     }
359
360     return evaluate(context(), v8function, isUndefined);
361 }
362
363 QVariant QQmlExpressionPrivate::value(bool *isUndefined)
364 {
365     Q_Q(QQmlExpression);
366
367     if (!context() || !context()->isValid()) {
368         qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
369         return QVariant();
370     }
371
372     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
373     QVariant rv;
374
375     ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
376
377     {
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*> >());
382     }
383
384     ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
385
386     return rv;
387 }
388
389 /*!
390     Evaulates the expression, returning the result of the evaluation,
391     or an invalid QVariant if the expression is invalid or has an error.
392
393     \a valueIsUndefined is set to true if the expression resulted in an
394     undefined value.
395
396     \sa hasError(), error()
397 */
398 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
399 {
400     Q_D(QQmlExpression);
401     return d->value(valueIsUndefined);
402 }
403
404 /*!
405 Returns true if the valueChanged() signal is emitted when the expression's evaluated
406 value changes.
407 */
408 bool QQmlExpression::notifyOnValueChanged() const
409 {
410     Q_D(const QQmlExpression);
411     return d->notifyOnValueChanged();
412 }
413
414 /*!
415   Sets whether the valueChanged() signal is emitted when the
416   expression's evaluated value changes.
417
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.
423
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.
429 */
430 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
431 {
432     Q_D(QQmlExpression);
433     d->setNotifyOnValueChanged(notifyOnChange);
434 }
435
436 /*!
437     Returns the source file URL for this expression.  The source location must
438     have been previously set by calling setSourceLocation().
439 */
440 QString QQmlExpression::sourceFile() const
441 {
442     Q_D(const QQmlExpression);
443     return d->url;
444 }
445
446 /*!
447     Returns the source file line number for this expression.  The source location
448     must have been previously set by calling setSourceLocation().
449 */
450 int QQmlExpression::lineNumber() const
451 {
452     Q_D(const QQmlExpression);
453     return d->line;
454 }
455
456 /*!
457     Returns the source file column number for this expression.  The source location
458     must have been previously set by calling setSourceLocation().
459 */
460 int QQmlExpression::columnNumber() const
461 {
462     Q_D(const QQmlExpression);
463     return d->column;
464 }
465
466 /*!
467     Set the location of this expression to \a line of \a url. This information
468     is used by the script engine.
469 */
470 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
471 {
472     Q_D(QQmlExpression);
473     d->url = url;
474     d->line = line;
475     d->column = column;
476 }
477
478 /*!
479     Returns the expression's scope object, if provided, otherwise 0.
480
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.
483 */
484 QObject *QQmlExpression::scopeObject() const
485 {
486     Q_D(const QQmlExpression);
487     return d->scopeObject();
488 }
489
490 /*!
491     Returns true if the last call to evaluate() resulted in an error,
492     otherwise false.
493
494     \sa error(), clearError()
495 */
496 bool QQmlExpression::hasError() const
497 {
498     Q_D(const QQmlExpression);
499     return d->hasError();
500 }
501
502 /*!
503     Clear any expression errors.  Calls to hasError() following this will
504     return false.
505
506     \sa hasError(), error()
507 */
508 void QQmlExpression::clearError()
509 {
510     Q_D(QQmlExpression);
511     d->clearError();
512 }
513
514 /*!
515     Return any error from the last call to evaluate().  If there was no error,
516     this returns an invalid QQmlError instance.
517
518     \sa hasError(), clearError()
519 */
520
521 QQmlError QQmlExpression::error() const
522 {
523     Q_D(const QQmlExpression);
524     return d->error(engine());
525 }
526
527 /*!
528     \fn void QQmlExpression::valueChanged()
529
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.
533 */
534
535 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
536 {
537     QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
538     This->expressionChanged();
539 }
540
541 void QQmlExpressionPrivate::expressionChanged()
542 {
543     Q_Q(QQmlExpression);
544     emit q->valueChanged();
545 }
546
547 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
548 {
549     QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
550     return QLatin1Char('"') + This->expression + QLatin1Char('"');
551 }
552
553 QT_END_NAMESPACE
554
555 #include <moc_qqmlexpression.cpp>