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