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 \note The resulting expression can always be evaluated, but its source code,
100 as returned by expression(), may not be available. If the QML module that
101 produced \a script was compiled ahead of time, the source code of
102 non-literal script strings can be discarded. See expression() for details.
103
104 \sa QQmlScriptString
105*/
106QQmlExpression::QQmlExpression(const QQmlScriptString &script, QQmlContext *ctxt, QObject *scope, QObject *parent)
107: QObject(*new QQmlExpressionPrivate, parent)
108{
109 Q_D(QQmlExpression);
110 if (ctxt && !ctxt->isValid())
111 return;
112
113 const QQmlScriptStringPrivate *scriptPrivate = script.d.data();
114 if (!scriptPrivate) {
115 // A null QQmlScriptStringPrivate is an empty expression without context.
116 // We may still want the explicitly passed context, though.
117 if (ctxt)
118 d->init(QQmlContextData::get(ctxt), QString(), scope);
119 return;
120 }
121
122 if (!ctxt && (!scriptPrivate->context || !scriptPrivate->context->isValid()))
123 return;
124
125 QQmlRefPointer<QQmlContextData> evalCtxtData
126 = QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context);
127 QObject *scopeObject = scope ? scope : scriptPrivate->scope;
128 QV4::Function *runtimeFunction = nullptr;
129
130 if (scriptPrivate->context) {
131 QQmlRefPointer<QQmlContextData> ctxtdata = QQmlContextData::get(scriptPrivate->context);
132 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(scriptPrivate->context->engine());
133 if (engine
134 && ctxtdata
135 && !ctxtdata->urlString().isEmpty()
136 && ctxtdata->typeCompilationUnit()) {
137 d->url = ctxtdata->urlString();
138 d->line = scriptPrivate->lineNumber;
139 d->column = scriptPrivate->columnNumber;
140
141 if (scriptPrivate->bindingId != QQmlBinding::Invalid)
142 runtimeFunction = ctxtdata->typeCompilationUnit()->runtimeFunctions.at(scriptPrivate->bindingId);
143 }
144 }
145
146 if (runtimeFunction) {
147 d->expression = scriptPrivate->script;
148 d->init(evalCtxtData, runtimeFunction, scopeObject);
149 } else
150 d->init(evalCtxtData, scriptPrivate->script, scopeObject);
151}
152
153/*!
154 Create a QQmlExpression object that is a child of \a parent.
155
156 The \a expression JavaScript will be executed in the \a ctxt QQmlContext.
157 If specified, the \a scope object's properties will also be in scope during
158 the expression's execution.
159*/
160QQmlExpression::QQmlExpression(QQmlContext *ctxt, QObject *scope, const QString &expression,
161 QObject *parent)
162: QObject(*new QQmlExpressionPrivate, parent)
163{
164 Q_D(QQmlExpression);
165 d->init(QQmlContextData::get(ctxt), expression, scope);
166}
167
168/*!
169 \internal
170*/
171QQmlExpression::QQmlExpression(QQmlExpressionPrivate &dd, QObject *parent) : QObject(dd, parent)
172{
173// Q_D(QQmlExpression);
174// d->init(QQmlContextData::get(ctxt), expression, scope);
175}
176
177/*!
178 Destroy the QQmlExpression instance.
179*/
180QQmlExpression::~QQmlExpression()
181{
182}
183
184/*!
185 Returns the QQmlEngine this expression is associated with, or \nullptr if there
186 is no association or the QQmlEngine has been destroyed.
187*/
188QQmlEngine *QQmlExpression::engine() const
189{
190 Q_D(const QQmlExpression);
191 return d->engine();
192}
193
194/*!
195 Returns the QQmlContext this expression is associated with, or \nullptr if there
196 is no association or the QQmlContext has been destroyed.
197*/
198QQmlContext *QQmlExpression::context() const
199{
200 Q_D(const QQmlExpression);
201 return d->publicContext();
202}
203
204/*!
205 Returns the expression string.
206
207 If this expression was constructed from a \l QQmlScriptString, its source
208 code is not guaranteed to be available. A QML module compiled ahead of time
209 may not retain the source code of non-literal script strings. In that case
210 the expression can still be evaluated via evaluate(), but expression()
211 returns an empty string and produces a warning. Only literals (numbers,
212 strings, booleans, \c null and \c undefined) are guaranteed to be available
213 as source code.
214*/
215QString QQmlExpression::expression() const
216{
217 Q_D(const QQmlExpression);
218 if (d->expressionFunctionValid) {
219 qWarning("QQmlExpression: Attempted to retrieve potentially discarded source code of "
220 "expression. Only literals (numbers, strings, booleans, null, undefined) are "
221 "guaranteed to be available from QQmlExpression::expression().");
222 }
223 return d->expression;
224}
225
226/*!
227 Set the expression to \a expression.
228*/
229void QQmlExpression::setExpression(const QString &expression)
230{
231 Q_D(QQmlExpression);
232
233 d->resetNotifyOnValueChanged();
234 d->expression = expression;
235 d->expressionFunctionValid = false;
236}
237
238// Must be called with a valid handle scope
240{
242 createQmlBinding(context(), scopeObject(), expression, url, line);
244 if (hasError()) {
245 if (isUndefined)
246 *isUndefined = true;
247 return QV4::Encode::undefined();
248 }
249 }
250
251 return evaluate(isUndefined);
252}
253
254QVariant QQmlExpressionPrivate::value(bool *isUndefined)
255{
256 Q_Q(QQmlExpression);
257
258 if (!hasValidContext()) {
259 qWarning("QQmlExpression: Attempted to evaluate an expression in an invalid context");
260 return QVariant();
261 }
262
263 QQmlEngine *engine = q->engine();
264 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
265 QVariant rv;
266
267 ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
268
269 {
270 QV4::Scope scope(engine->handle());
271 QV4::ScopedValue result(scope, v4value(isUndefined));
272 if (!hasError())
273 rv = QV4::ExecutionEngine::toVariant(result, QMetaType {});
274 }
275
276 ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
277
278 return rv;
279}
280
281/*!
282 Evaulates the expression, returning the result of the evaluation,
283 or an invalid QVariant if the expression is invalid or has an error.
284
285 \a valueIsUndefined is set to true if the expression resulted in an
286 undefined value.
287
288 \sa hasError(), error()
289*/
290QVariant QQmlExpression::evaluate(bool *valueIsUndefined)
291{
292 Q_D(QQmlExpression);
293 return d->value(valueIsUndefined);
294}
295
296/*!
297Returns true if the valueChanged() signal is emitted when the expression's evaluated
298value changes.
299*/
300bool QQmlExpression::notifyOnValueChanged() const
301{
302 Q_D(const QQmlExpression);
303 return d->notifyOnValueChanged();
304}
305
306/*!
307 Sets whether the valueChanged() signal is emitted when the
308 expression's evaluated value changes.
309
310 If \a notifyOnChange is true, the QQmlExpression will
311 monitor properties involved in the expression's evaluation, and emit
312 QQmlExpression::valueChanged() if they have changed. This
313 allows an application to ensure that any value associated with the
314 result of the expression remains up to date.
315
316 If \a notifyOnChange is false (default), the QQmlExpression
317 will not montitor properties involved in the expression's
318 evaluation, and QQmlExpression::valueChanged() will never be
319 emitted. This is more efficient if an application wants a "one off"
320 evaluation of the expression.
321*/
322void QQmlExpression::setNotifyOnValueChanged(bool notifyOnChange)
323{
324 Q_D(QQmlExpression);
325 d->setNotifyOnValueChanged(notifyOnChange);
326}
327
328/*!
329 Returns the source file URL for this expression. The source location must
330 have been previously set by calling setSourceLocation().
331*/
332QString QQmlExpression::sourceFile() const
333{
334 Q_D(const QQmlExpression);
335 return d->url;
336}
337
338/*!
339 Returns the source file line number for this expression. The source location
340 must have been previously set by calling setSourceLocation().
341*/
342int QQmlExpression::lineNumber() const
343{
344 Q_D(const QQmlExpression);
345 return qmlConvertSourceCoordinate<quint16, int>(d->line);
346}
347
348/*!
349 Returns the source file column number for this expression. The source location
350 must have been previously set by calling setSourceLocation().
351*/
352int QQmlExpression::columnNumber() const
353{
354 Q_D(const QQmlExpression);
355 return qmlConvertSourceCoordinate<quint16, int>(d->column);
356}
357
358/*!
359 Set the location of this expression to \a line and \a column of \a url. This information
360 is used by the script engine.
361*/
362void QQmlExpression::setSourceLocation(const QString &url, int line, int column)
363{
364 Q_D(QQmlExpression);
365 d->url = url;
366 d->line = qmlConvertSourceCoordinate<int, quint16>(line);
367 d->column = qmlConvertSourceCoordinate<int, quint16>(column);
368}
369
370/*!
371 Returns the expression's scope object, if provided, otherwise 0.
372
373 In addition to data provided by the expression's QQmlContext, the scope
374 object's properties are also in scope during the expression's evaluation.
375*/
376QObject *QQmlExpression::scopeObject() const
377{
378 Q_D(const QQmlExpression);
379 return d->scopeObject();
380}
381
382/*!
383 Returns true if the last call to evaluate() resulted in an error,
384 otherwise false.
385
386 \sa error(), clearError()
387*/
388bool QQmlExpression::hasError() const
389{
390 Q_D(const QQmlExpression);
391 return d->hasError();
392}
393
394/*!
395 Clear any expression errors. Calls to hasError() following this will
396 return false.
397
398 \sa hasError(), error()
399*/
400void QQmlExpression::clearError()
401{
402 Q_D(QQmlExpression);
403 d->clearError();
404}
405
406/*!
407 Return any error from the last call to evaluate(). If there was no error,
408 this returns an invalid QQmlError instance.
409
410 \sa hasError(), clearError()
411*/
412
413QQmlError QQmlExpression::error() const
414{
415 Q_D(const QQmlExpression);
416 return d->error(engine());
417}
418
419/*!
420 \fn void QQmlExpression::valueChanged()
421
422 Emitted each time the expression value changes from the last time it was
423 evaluated. The expression must have been evaluated at least once (by
424 calling QQmlExpression::evaluate()) before this signal will be emitted.
425*/
426
428{
429 Q_Q(QQmlExpression);
430 emit q->valueChanged();
431}
432
434{
435 return QLatin1Char('"') + expression + QLatin1Char('"');
436}
437
438QT_END_NAMESPACE
439
440#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