Merge master into api_changes
[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   extractExpressionFromFunction(false), line(-1), dataRef(0)
64 {
65 }
66
67 QQmlExpressionPrivate::~QQmlExpressionPrivate()
68 {
69     qPersistentDispose(v8qmlscope);
70     qPersistentDispose(v8function);
71     if (dataRef) dataRef->release();
72     dataRef = 0;
73 }
74
75 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me)
76 {
77     expression = expr;
78
79     QQmlAbstractExpression::setContext(ctxt);
80     setScopeObject(me);
81     expressionFunctionValid = false;
82     expressionFunctionRewritten = false;
83 }
84
85 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr,
86                                  bool isRewritten, QObject *me, const QString &srcUrl,
87                                  int lineNumber, int columnNumber)
88 {
89     url = srcUrl;
90     line = lineNumber;
91     column = columnNumber;
92
93     expression = expr;
94
95     expressionFunctionValid = false;
96     expressionFunctionRewritten = isRewritten;
97
98     QQmlAbstractExpression::setContext(ctxt);
99     setScopeObject(me);
100 }
101
102 void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QByteArray &expr,
103                                  bool isRewritten, QObject *me, const QString &srcUrl,
104                                  int lineNumber, int columnNumber)
105 {
106     url = srcUrl;
107     line = lineNumber;
108     column = columnNumber;
109
110     if (isRewritten) {
111         expressionFunctionValid = true;
112         expressionFunctionRewritten = true;
113         v8function = evalFunction(ctxt, me, expr.constData(), expr.length(),
114                                   srcUrl, lineNumber, &v8qmlscope);
115         setUseSharedContext(false);
116
117         expressionUtf8 = expr;
118     } else {
119         expression = QString::fromUtf8(expr);
120
121         expressionFunctionValid = false;
122         expressionFunctionRewritten = isRewritten;
123     }
124
125     QQmlAbstractExpression::setContext(ctxt);
126     setScopeObject(me);
127 }
128
129 QQmlExpression *
130 QQmlExpressionPrivate::create(QQmlContextData *ctxt, QObject *object,
131                               const QString &expr, bool isRewritten,
132                               const QString &url, int lineNumber, int columnNumber)
133 {
134     return new QQmlExpression(ctxt, object, expr, isRewritten, url, lineNumber, columnNumber,
135                               *new QQmlExpressionPrivate);
136 }
137
138 /*!
139     \class QQmlExpression
140     \since 5.0
141     \inmodule QtQml
142     \brief The QQmlExpression class evaluates JavaScript in a QML context.
143
144     For example, given a file \c main.qml like this:
145
146     \qml
147     import QtQuick 2.0
148
149     Item {
150         width: 200; height: 200
151     }
152     \endqml
153
154     The following code evaluates a JavaScript expression in the context of the
155     above QML:
156
157     \code
158     QQmlEngine *engine = new QQmlEngine;
159     QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
160
161     QObject *myObject = component.create();
162     QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
163     int result = expr->evaluate().toInt();  // result = 400
164     \endcode
165
166     Note that the QtQuick 1 version is called QDeclarativeExpression.
167 */
168
169 /*!
170     Create an invalid QQmlExpression.
171
172     As the expression will not have an associated QQmlContext, this will be a
173     null expression object and its value will always be an invalid QVariant.
174  */
175 QQmlExpression::QQmlExpression()
176 : QObject(*new QQmlExpressionPrivate, 0)
177 {
178 }
179
180 /*!  \internal */
181 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, 
182                                                QObject *object, const QString &expr, bool isRewritten,
183                                                const QString &url, int lineNumber, int columnNumber,
184                                                QQmlExpressionPrivate &dd)
185 : QObject(dd, 0)
186 {
187     Q_D(QQmlExpression);
188     d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
189 }
190
191 /*!  \internal */
192 QQmlExpression::QQmlExpression(QQmlContextData *ctxt,
193                                                QObject *object, const QByteArray &expr,
194                                                bool isRewritten,
195                                                const QString &url, int lineNumber, int columnNumber,
196                                                QQmlExpressionPrivate &dd)
197 : QObject(dd, 0)
198 {
199     Q_D(QQmlExpression);
200     d->init(ctxt, expr, isRewritten, object, url, lineNumber, columnNumber);
201 }
202
203 /*!
204     Create a QQmlExpression object that is a child of \a parent.
205
206     The \script provides the expression to be evaluated, the context to evaluate it in,
207     and the scope object to evaluate it with.
208
209     This constructor is functionally equivalent to the following, but in most cases
210     is more efficient.
211     \code
212     QQmlExpression expression(script.context(), script.scopeObject(), script.script(), parent);
213     \endcode
214
215     \sa QQmlScriptString
216 */
217 QQmlExpression::QQmlExpression(const QQmlScriptString &script, QObject *parent)
218 : QObject(*new QQmlExpressionPrivate, parent)
219 {
220     Q_D(QQmlExpression);
221     bool defaultConstruction = false;
222
223     int id = script.d.data()->bindingId;
224     if (id < 0) {
225         defaultConstruction = true;
226     } else {
227         QQmlContextData *ctxtdata = QQmlContextData::get(script.context());
228
229         QQmlEnginePrivate *engine = QQmlEnginePrivate::get(script.context()->engine());
230         QQmlCompiledData *cdata = 0;
231         QQmlTypeData *typeData = 0;
232         if (engine && ctxtdata && !ctxtdata->url.isEmpty()) {
233             typeData = engine->typeLoader.get(ctxtdata->url);
234             cdata = typeData->compiledData();
235         }
236
237         if (cdata)
238             d->init(ctxtdata, cdata->primitives.at(id), true, script.scopeObject(),
239                     cdata->name, script.d.data()->lineNumber, script.d.data()->columnNumber);
240         else
241            defaultConstruction = true;
242
243         if (cdata)
244             cdata->release();
245         if (typeData)
246             typeData->release();
247     }
248
249     if (defaultConstruction)
250         d->init(QQmlContextData::get(script.context()), script.script(), script.scopeObject());
251 }
252
253 /*!
254     Create a QQmlExpression object that is a child of \a parent.
255
256     The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
257     If specified, the \a scope object's properties will also be in scope during
258     the expression's execution.
259 */
260 QQmlExpression::QQmlExpression(QQmlContext *ctxt,
261                                                QObject *scope,
262                                                const QString &expression,
263                                                QObject *parent)
264 : QObject(*new QQmlExpressionPrivate, parent)
265 {
266     Q_D(QQmlExpression);
267     d->init(QQmlContextData::get(ctxt), expression, scope);
268 }
269
270 /*! 
271     \internal
272 */
273 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
274                                                const QString &expression)
275 : QObject(*new QQmlExpressionPrivate, 0)
276 {
277     Q_D(QQmlExpression);
278     d->init(ctxt, expression, scope);
279 }
280
281 /*!  \internal */
282 QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
283                                                const QString &expression, QQmlExpressionPrivate &dd)
284 : QObject(dd, 0)
285 {
286     Q_D(QQmlExpression);
287     d->init(ctxt, expression, scope);
288 }
289
290 /*!
291     Destroy the QQmlExpression instance.
292 */
293 QQmlExpression::~QQmlExpression()
294 {
295 }
296
297 /*!
298     Returns the QQmlEngine this expression is associated with, or 0 if there
299     is no association or the QQmlEngine has been destroyed.
300 */
301 QQmlEngine *QQmlExpression::engine() const
302 {
303     Q_D(const QQmlExpression);
304     return d->context()?d->context()->engine:0;
305 }
306
307 /*!
308     Returns the QQmlContext this expression is associated with, or 0 if there
309     is no association or the QQmlContext has been destroyed.
310 */
311 QQmlContext *QQmlExpression::context() const
312 {
313     Q_D(const QQmlExpression);
314     QQmlContextData *data = d->context();
315     return data?data->asQQmlContext():0;
316 }
317
318 /*!
319     Returns the expression string.
320 */
321 QString QQmlExpression::expression() const
322 {
323     Q_D(const QQmlExpression);
324     if (d->extractExpressionFromFunction && context()->engine()) {
325         QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(context()->engine());
326         v8::HandleScope handle_scope;
327         v8::Context::Scope scope(v8engine->context());
328
329         return v8engine->toString(v8::Handle<v8::Value>(d->v8function));
330     } else if (!d->expressionUtf8.isEmpty()) {
331         return QString::fromUtf8(d->expressionUtf8);
332     } else {
333         return d->expression;
334     }
335 }
336
337 /*!
338     Set the expression to \a expression.
339 */
340 void QQmlExpression::setExpression(const QString &expression)
341 {
342     Q_D(QQmlExpression);
343
344     d->resetNotifyOnValueChanged();
345     d->expression = expression;
346     d->expressionUtf8.clear();
347     d->expressionFunctionValid = false;
348     d->expressionFunctionRewritten = false;
349     qPersistentDispose(d->v8function);
350     qPersistentDispose(d->v8qmlscope);
351 }
352
353 // Must be called with a valid handle scope
354 v8::Local<v8::Value> QQmlExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined)
355 {
356     if (!expressionFunctionValid) {
357         bool ok = true;
358
359         QQmlRewrite::RewriteBinding rewriteBinding;
360         rewriteBinding.setName(name);
361         QString code;
362         if (expressionFunctionRewritten)
363             code = expression;
364         else
365             code = rewriteBinding(expression, &ok);
366
367         if (ok) v8function = evalFunction(context(), scopeObject(), code, url, line, &v8qmlscope);
368         setUseSharedContext(false);
369         expressionFunctionValid = true;
370     }
371
372
373     if (secondaryScope) {
374         v8::Local<v8::Value> result;
375         QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine);
376         QObject *restoreSecondaryScope = 0;
377         restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope);
378         result = evaluate(context(), v8function, isUndefined);
379         ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope);
380         return result;
381     } else {
382         return evaluate(context(), v8function, isUndefined);
383     }
384 }
385
386 QVariant QQmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined)
387 {
388     Q_Q(QQmlExpression);
389
390     if (!context() || !context()->isValid()) {
391         qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
392         return QVariant();
393     }
394
395     QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine());
396     QVariant rv;
397
398     ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
399
400     {
401         v8::HandleScope handle_scope;
402         v8::Context::Scope context_scope(ep->v8engine()->context());
403         v8::Local<v8::Value> result = v8value(secondaryScope, isUndefined);
404         rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >());
405     }
406
407     ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
408
409     return rv;
410 }
411
412 /*!
413     Evaulates the expression, returning the result of the evaluation,
414     or an invalid QVariant if the expression is invalid or has an error.
415
416     \a valueIsUndefined is set to true if the expression resulted in an
417     undefined value.
418
419     \sa hasError(), error()
420 */
421 QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
422 {
423     Q_D(QQmlExpression);
424     return d->value(0, valueIsUndefined);
425 }
426
427 /*!
428 Returns true if the valueChanged() signal is emitted when the expression's evaluated
429 value changes.
430 */
431 bool QQmlExpression::notifyOnValueChanged() const
432 {
433     Q_D(const QQmlExpression);
434     return d->notifyOnValueChanged();
435 }
436
437 /*!
438   Sets whether the valueChanged() signal is emitted when the
439   expression's evaluated value changes.
440
441   If \a notifyOnChange is true, the QQmlExpression will
442   monitor properties involved in the expression's evaluation, and emit
443   QQmlExpression::valueChanged() if they have changed.  This
444   allows an application to ensure that any value associated with the
445   result of the expression remains up to date.
446
447   If \a notifyOnChange is false (default), the QQmlExpression
448   will not montitor properties involved in the expression's
449   evaluation, and QQmlExpression::valueChanged() will never be
450   emitted.  This is more efficient if an application wants a "one off"
451   evaluation of the expression.
452 */
453 void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
454 {
455     Q_D(QQmlExpression);
456     d->setNotifyOnValueChanged(notifyOnChange);
457 }
458
459 /*!
460     Returns the source file URL for this expression.  The source location must
461     have been previously set by calling setSourceLocation().
462 */
463 QString QQmlExpression::sourceFile() const
464 {
465     Q_D(const QQmlExpression);
466     return d->url;
467 }
468
469 /*!
470     Returns the source file line number for this expression.  The source location 
471     must have been previously set by calling setSourceLocation().
472 */
473 int QQmlExpression::lineNumber() const
474 {
475     Q_D(const QQmlExpression);
476     return d->line;
477 }
478
479 /*!
480     Returns the source file column number for this expression.  The source location
481     must have been previously set by calling setSourceLocation().
482 */
483 int QQmlExpression::columnNumber() const
484 {
485     Q_D(const QQmlExpression);
486     return d->column;
487 }
488
489 /*!
490     Set the location of this expression to \a line of \a url. This information
491     is used by the script engine.
492 */
493 void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
494 {
495     Q_D(QQmlExpression);
496     d->url = url;
497     d->line = line;
498     d->column = column;
499 }
500
501 /*!
502     Returns the expression's scope object, if provided, otherwise 0.
503
504     In addition to data provided by the expression's QQmlContext, the scope
505     object's properties are also in scope during the expression's evaluation.
506 */
507 QObject *QQmlExpression::scopeObject() const
508 {
509     Q_D(const QQmlExpression);
510     return d->scopeObject();
511 }
512
513 /*!
514     Returns true if the last call to evaluate() resulted in an error,
515     otherwise false.
516     
517     \sa error(), clearError()
518 */
519 bool QQmlExpression::hasError() const
520 {
521     Q_D(const QQmlExpression);
522     return d->hasError();
523 }
524
525 /*!
526     Clear any expression errors.  Calls to hasError() following this will
527     return false.
528
529     \sa hasError(), error()
530 */
531 void QQmlExpression::clearError()
532 {
533     Q_D(QQmlExpression);
534     d->clearError();
535 }
536
537 /*!
538     Return any error from the last call to evaluate().  If there was no error,
539     this returns an invalid QQmlError instance.
540
541     \sa hasError(), clearError()
542 */
543
544 QQmlError QQmlExpression::error() const
545 {
546     Q_D(const QQmlExpression);
547     return d->error();
548 }
549
550 /*!
551     \fn void QQmlExpression::valueChanged()
552
553     Emitted each time the expression value changes from the last time it was
554     evaluated.  The expression must have been evaluated at least once (by
555     calling QQmlExpression::evaluate()) before this signal will be emitted.
556 */
557
558 void QQmlExpressionPrivate::expressionChanged(QQmlJavaScriptExpression *e)
559 {
560     QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
561     This->expressionChanged();
562 }
563
564 void QQmlExpressionPrivate::expressionChanged()
565 {
566     Q_Q(QQmlExpression);
567     emit q->valueChanged();
568 }
569
570 QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e)
571 {
572     QQmlExpressionPrivate *This = static_cast<QQmlExpressionPrivate *>(e);
573     return QLatin1String("\"") + This->expression + QLatin1String("\"");
574 }
575
576 QT_END_NAMESPACE
577
578 #include <moc_qqmlexpression.cpp>