1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlexpression.h"
41#include "qqmlexpression_p.h"
42
43#include "qqmlglobal_p.h"
44#include "qqmlengine_p.h"
45#include "qqmlcontext_p.h"
46#include "qqmlscriptstring_p.h"
47#include "qqmlbinding_p.h"
48#include <private/qqmlsourcecoordinate_p.h>
49#include <private/qv4qmlcontext_p.h>
50
51#include <QtCore/qdebug.h>
52
53QT_BEGIN_NAMESPACE
54
55QQmlExpressionPrivate::QQmlExpressionPrivate()
56: QQmlJavaScriptExpression(),
57 expressionFunctionValid(true),
58 line(0), column(0)
59{
60}
61
62QQmlExpressionPrivate::~QQmlExpressionPrivate()
63{
64}
65
66void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me)
67{
68 expression = expr;
69
70 QQmlJavaScriptExpression::setContext(ctxt);
71 setScopeObject(me);
72 expressionFunctionValid = false;
73}
74
75void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFunction, QObject *me)
76{
77 expressionFunctionValid = true;
78 QV4::ExecutionEngine *engine = ctxt->engine->handle();
79 QV4::Scope scope(engine);
80 QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(parent: engine->rootContext(), context: ctxt, scopeObject: me));
81 setupFunction(qmlContext, f: runtimeFunction);
82
83 QQmlJavaScriptExpression::setContext(ctxt);
84 setScopeObject(me);
85}
86
87/*!
88 \class QQmlExpression
89 \since 5.0
90 \inmodule QtQml
91 \brief The QQmlExpression class evaluates JavaScript in a QML context.
92
93 For example, given a file \c main.qml like this:
94
95 \qml
96 import QtQuick 2.0
97
98 Item {
99 width: 200; height: 200
100 }
101 \endqml
102
103 The following code evaluates a JavaScript expression in the context of the
104 above QML:
105
106 \code
107 QQmlEngine *engine = new QQmlEngine;
108 QQmlComponent component(engine, QUrl::fromLocalFile("main.qml"));
109
110 QObject *myObject = component.create();
111 QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
112 int result = expr->evaluate().toInt(); // result = 400
113 \endcode
114*/
115
116/*!
117 Create an invalid QQmlExpression.
118
119 As the expression will not have an associated QQmlContext, this will be a
120 null expression object and its value will always be an invalid QVariant.
121 */
122QQmlExpression::QQmlExpression()
123: QObject(*new QQmlExpressionPrivate, nullptr)
124{
125}
126
127/*!
128 Create a QQmlExpression object that is a child of \a parent.
129
130 The \a script provides the expression to be evaluated, the context to evaluate it in,
131 and the scope object to evaluate it with. If provided, \a ctxt and \a scope will override
132 the context and scope object provided by \a script.
133
134 \sa QQmlScriptString
135*/
136QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt, QObject *scope, QObject *parent)
137: QObject(*new QQmlExpressionPrivate, parent)
138{
139 Q_D(QQmlExpression);
140 if (ctxt && !ctxt->isValid())
141 return;
142
143 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
144 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
145 return;
146
147 QQmlContextData *evalCtxtData = QQmlContextData::get(context: ctxt ? ctxt : scriptPrivate->context);
148 QObject *scopeObject = scope ? scope : scriptPrivate->scope;
149 QV4::Function *runtimeFunction = nullptr;
150
151 if (scriptPrivate->context) {
152 QQmlContextData *ctxtdata = QQmlContextData::get(context: scriptPrivate->context);
153 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(e: scriptPrivate->context->engine());
154 if (engine && ctxtdata && !ctxtdata->urlString().isEmpty() && ctxtdata->typeCompilationUnit) {
155 d->url = ctxtdata->urlString();
156 d->line = scriptPrivate->lineNumber;
157 d->column = scriptPrivate->columnNumber;
158
159 if (scriptPrivate->bindingId != QQmlBinding::Invalid)
160 runtimeFunction = ctxtdata->typeCompilationUnit->runtimeFunctions.at(i: scriptPrivate->bindingId);
161 }
162 }
163
164 if (runtimeFunction) {
165 d->expression = scriptPrivate->script;
166 d->init(ctxt: evalCtxtData, runtimeFunction, me: scopeObject);
167 } else
168 d->init(ctxt: evalCtxtData, expr: scriptPrivate->script, me: scopeObject);
169}
170
171/*!
172 Create a QQmlExpression object that is a child of \a parent.
173
174 The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
175 If specified, the \a scope object's properties will also be in scope during
176 the expression's execution.
177*/
178QQmlExpression::QQmlExpression(QQmlContext *ctxt,
179 QObject *scope,
180 const QString &expression,
181 QObject *parent)
182: QObject(*new QQmlExpressionPrivate, parent)
183{
184 Q_D(QQmlExpression);
185 d->init(ctxt: QQmlContextData::get(context: ctxt), expr: expression, me: scope);
186}
187
188/*!
189 \internal
190*/
191QQmlExpression::QQmlExpression(QQmlContextData *ctxt, QObject *scope,
192 const QString &expression)
193: QObject(*new QQmlExpressionPrivate, nullptr)
194{
195 Q_D(QQmlExpression);
196 d->init(ctxt, expr: expression, me: scope);
197}
198
199/*!
200 Destroy the QQmlExpression instance.
201*/
202QQmlExpression::~QQmlExpression()
203{
204}
205
206/*!
207 Returns the QQmlEngine this expression is associated with, or \nullptr if there
208 is no association or the QQmlEngine has been destroyed.
209*/
210QQmlEngine *QQmlExpression::engine() const
211{
212 Q_D(const QQmlExpression);
213 return d->context()?d->context()->engine:nullptr;
214}
215
216/*!
217 Returns the QQmlContext this expression is associated with, or \nullptr if there
218 is no association or the QQmlContext has been destroyed.
219*/
220QQmlContext *QQmlExpression::context() const
221{
222 Q_D(const QQmlExpression);
223 QQmlContextData *data = d->context();
224 return data?data->asQQmlContext():nullptr;
225}
226
227/*!
228 Returns the expression string.
229*/
230QString QQmlExpression::expression() const
231{
232 Q_D(const QQmlExpression);
233 return d->expression;
234}
235
236/*!
237 Set the expression to \a expression.
238*/
239void QQmlExpression::setExpression(const QString &expression)
240{
241 Q_D(QQmlExpression);
242
243 d->resetNotifyOnValueChanged();
244 d->expression = expression;
245 d->expressionFunctionValid = false;
246}
247
248// Must be called with a valid handle scope
249QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined)
250{
251 if (!expressionFunctionValid) {
252 createQmlBinding(ctxt: context(), scope: scopeObject(), code: expression, filename: url, line);
253 expressionFunctionValid = true;
254 if (hasError()) {
255 if (isUndefined)
256 *isUndefined = true;
257 return QV4::Encode::undefined();
258 }
259 }
260
261 return evaluate(isUndefined);
262}
263
264QVariant QQmlExpressionPrivate::value(bool *isUndefined)
265{
266 Q_Q(QQmlExpression);
267
268 if (!context() || !context()->isValid()) {
269 qWarning(msg: "QQmlExpression: Attempted to evaluate an expression in an invalid context");
270 return QVariant();
271 }
272
273 QQmlEngine *engine = q->engine();
274 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine);
275 QVariant rv;
276
277 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
278
279 {
280 QV4::Scope scope(engine->handle());
281 QV4::ScopedValue result(scope, v4value(isUndefined));
282 if (!hasError())
283 rv = scope.engine->toVariant(value: result, typeHint: -1);
284 }
285
286 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
287
288 return rv;
289}
290
291/*!
292 Evaulates the expression, returning the result of the evaluation,
293 or an invalid QVariant if the expression is invalid or has an error.
294
295 \a valueIsUndefined is set to true if the expression resulted in an
296 undefined value.
297
298 \sa hasError(), error()
299*/
300QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
301{
302 Q_D(QQmlExpression);
303 return d->value(isUndefined: valueIsUndefined);
304}
305
306/*!
307Returns true if the valueChanged() signal is emitted when the expression's evaluated
308value changes.
309*/
310bool QQmlExpression::notifyOnValueChanged() const
311{
312 Q_D(const QQmlExpression);
313 return d->notifyOnValueChanged();
314}
315
316/*!
317 Sets whether the valueChanged() signal is emitted when the
318 expression's evaluated value changes.
319
320 If \a notifyOnChange is true, the QQmlExpression will
321 monitor properties involved in the expression's evaluation, and emit
322 QQmlExpression::valueChanged() if they have changed. This
323 allows an application to ensure that any value associated with the
324 result of the expression remains up to date.
325
326 If \a notifyOnChange is false (default), the QQmlExpression
327 will not montitor properties involved in the expression's
328 evaluation, and QQmlExpression::valueChanged() will never be
329 emitted. This is more efficient if an application wants a "one off"
330 evaluation of the expression.
331*/
332void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
333{
334 Q_D(QQmlExpression);
335 d->setNotifyOnValueChanged(notifyOnChange);
336}
337
338/*!
339 Returns the source file URL for this expression. The source location must
340 have been previously set by calling setSourceLocation().
341*/
342QString QQmlExpression::sourceFile() const
343{
344 Q_D(const QQmlExpression);
345 return d->url;
346}
347
348/*!
349 Returns the source file line number for this expression. The source location
350 must have been previously set by calling setSourceLocation().
351*/
352int QQmlExpression::lineNumber() const
353{
354 Q_D(const QQmlExpression);
355 return qmlConvertSourceCoordinate<quint16, int>(n: d->line);
356}
357
358/*!
359 Returns the source file column number for this expression. The source location
360 must have been previously set by calling setSourceLocation().
361*/
362int QQmlExpression::columnNumber() const
363{
364 Q_D(const QQmlExpression);
365 return qmlConvertSourceCoordinate<quint16, int>(n: d->column);
366}
367
368/*!
369 Set the location of this expression to \a line and \a column of \a url. This information
370 is used by the script engine.
371*/
372void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
373{
374 Q_D(QQmlExpression);
375 d->url = url;
376 d->line = qmlConvertSourceCoordinate<int, quint16>(n: line);
377 d->column = qmlConvertSourceCoordinate<int, quint16>(n: column);
378}
379
380/*!
381 Returns the expression's scope object, if provided, otherwise 0.
382
383 In addition to data provided by the expression's QQmlContext, the scope
384 object's properties are also in scope during the expression's evaluation.
385*/
386QObject *QQmlExpression::scopeObject() const
387{
388 Q_D(const QQmlExpression);
389 return d->scopeObject();
390}
391
392/*!
393 Returns true if the last call to evaluate() resulted in an error,
394 otherwise false.
395
396 \sa error(), clearError()
397*/
398bool QQmlExpression::hasError() const
399{
400 Q_D(const QQmlExpression);
401 return d->hasError();
402}
403
404/*!
405 Clear any expression errors. Calls to hasError() following this will
406 return false.
407
408 \sa hasError(), error()
409*/
410void QQmlExpression::clearError()
411{
412 Q_D(QQmlExpression);
413 d->clearError();
414}
415
416/*!
417 Return any error from the last call to evaluate(). If there was no error,
418 this returns an invalid QQmlError instance.
419
420 \sa hasError(), clearError()
421*/
422
423QQmlError QQmlExpression::error() const
424{
425 Q_D(const QQmlExpression);
426 return d->error(engine());
427}
428
429/*!
430 \fn void QQmlExpression::valueChanged()
431
432 Emitted each time the expression value changes from the last time it was
433 evaluated. The expression must have been evaluated at least once (by
434 calling QQmlExpression::evaluate()) before this signal will be emitted.
435*/
436
437void QQmlExpressionPrivate::expressionChanged()
438{
439 Q_Q(QQmlExpression);
440 emit q->valueChanged();
441}
442
443QString QQmlExpressionPrivate::expressionIdentifier() const
444{
445 return QLatin1Char('"') + expression + QLatin1Char('"');
446}
447
448QT_END_NAMESPACE
449
450#include <moc_qqmlexpression.cpp>
451

source code of qtdeclarative/src/qml/qml/qqmlexpression.cpp