Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmljavascriptexpression.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
6
7#include <private/qqmlexpression_p.h>
8#include <private/qv4context_p.h>
9#include <private/qv4value_p.h>
10#include <private/qv4functionobject_p.h>
11#include <private/qv4script_p.h>
12#include <private/qv4errorobject_p.h>
13#include <private/qv4scopedvalue_p.h>
14#include <private/qv4jscall_p.h>
15#include <private/qqmlglobal_p.h>
16#include <private/qv4qobjectwrapper_p.h>
17#include <private/qqmlbuiltinfunctions_p.h>
18#include <private/qqmlsourcecoordinate_p.h>
19#include <private/qqmlabstractbinding_p.h>
20#include <private/qqmlpropertybinding_p.h>
21#include <private/qproperty_p.h>
22
24
26{
27 if (!e) return false;
28
29 if (e->inProgressCreations == 0) return false; // Not in construction
30
31 if (prevError) return true; // Already in error chain
32
33 prevError = &e->erroredBindings;
34 nextError = e->erroredBindings;
35 e->erroredBindings = this;
36 if (nextError) nextError->prevError = &nextError;
37
38 return true;
39}
40
42{
43 m_error.setUrl(QUrl(sourceLocation.sourceFile));
44 m_error.setLine(qmlConvertSourceCoordinate<quint16, int>(sourceLocation.line));
45 m_error.setColumn(qmlConvertSourceCoordinate<quint16, int>(sourceLocation.column));
46}
47
49{
50 m_error.setDescription(description);
51}
52
54{
55 m_error.setObject(object);
56}
57
59{
60 m_error = engine->catchExceptionAsQmlError();
61}
62
63
65 : m_context(nullptr),
66 m_prevExpression(nullptr),
67 m_nextExpression(nullptr),
68 m_v4Function(nullptr)
69{
70}
71
73{
74 if (m_prevExpression) {
75 *m_prevExpression = m_nextExpression;
76 if (m_nextExpression)
77 m_nextExpression->m_prevExpression = m_prevExpression;
78 }
79
81 auto current = qpropertyChangeTriggers;
84 }
85
87 clearError();
88 if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
89 m_scopeObject.asT2()->_s = nullptr;
90}
91
93{
94 if (auto f = function()) {
95 QString url = f->sourceFile();
96 uint lineNumber = f->compiledFunction->location.line();
97 uint columnNumber = f->compiledFunction->location.column();
98 return url + QString::asprintf(":%u:%u", lineNumber, columnNumber);
99 }
100
101 return QStringLiteral("[native code]");
102}
103
110
115
117{
118 if (m_v4Function)
119 return m_v4Function->sourceLocation();
120 return QQmlSourceLocation();
121}
122
123void QQmlJavaScriptExpression::setContext(const QQmlRefPointer<QQmlContextData> &context)
124{
125 if (m_prevExpression) {
126 *m_prevExpression = m_nextExpression;
127 if (m_nextExpression)
128 m_nextExpression->m_prevExpression = m_prevExpression;
129 m_prevExpression = nullptr;
130 m_nextExpression = nullptr;
131 }
132
133 m_context = context.data();
134
135 if (context)
136 context->addExpression(this);
137}
138
142
144{
145 QQmlEngine *qmlengine = engine();
146 if (!qmlengine) {
147 if (isUndefined)
148 *isUndefined = true;
149 return QV4::Encode::undefined();
150 }
151
152 QV4::Scope scope(qmlengine->handle());
153 QV4::JSCallArguments jsCall(scope);
154
155 return evaluate(jsCall.callData(scope), isUndefined);
156}
157
159{
160 Q_DISABLE_COPY_MOVE(QQmlJavaScriptExpressionCapture)
161public:
163 : watcher(expression)
164 , capture(engine, expression, &watcher)
166 {
167 Q_ASSERT(expression->notifyOnValueChanged() || expression->activeGuards.isEmpty());
169 lastPropertyCapture = ep->propertyCapture;
170 ep->propertyCapture = expression->notifyOnValueChanged() ? &capture : nullptr;
171
172 if (expression->notifyOnValueChanged())
173 capture.guards.copyAndClearPrepend(expression->activeGuards);
174 }
175
177 {
178 if (capture.errorString) {
179 for (int ii = 0; ii < capture.errorString->size(); ++ii)
180 qWarning("%s", qPrintable(capture.errorString->at(ii)));
181 delete capture.errorString;
182 capture.errorString = nullptr;
183 }
184
186 g->Delete();
187
188 ep->propertyCapture = lastPropertyCapture;
189 }
190
191 bool catchException(const QV4::Scope &scope) const
192 {
193 if (scope.hasException()) {
194 if (watcher.wasDeleted())
195 scope.engine->catchException(); // ignore exception
196 else
198 return true;
199 }
200
201 if (!watcher.wasDeleted() && capture.expression->hasDelayedError())
202 capture.expression->delayedError()->clearError();
203 return false;
204 }
205
206private:
208 QQmlPropertyCapture capture;
210 QQmlPropertyCapture *lastPropertyCapture;
211};
212
214{
216 QV4::Function *v4Function = function();
217 if (!v4Function || !qmlEngine) {
218 if (isUndefined)
219 *isUndefined = true;
220 return QV4::Encode::undefined();
221 }
222
223 // All code that follows must check with watcher before it accesses data members
224 // incase we have been deleted.
226
227 QV4::Scope scope(qmlEngine->handle());
228
229 if (QObject *thisObject = scopeObject()) {
230 callData->thisObject = QV4::QObjectWrapper::wrap(scope.engine, thisObject);
231 if (callData->thisObject.isNullOrUndefined())
232 callData->thisObject = scope.engine->globalObject;
233 } else {
234 callData->thisObject = scope.engine->globalObject;
235 }
236
237 Q_ASSERT(m_qmlScope.valueRef());
238 QV4::ScopedValue result(scope, v4Function->call(
239 &(callData->thisObject.asValue<QV4::Value>()),
240 callData->argValues<QV4::Value>(), callData->argc(),
241 static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef())));
242
243 if (capture.catchException(scope)) {
244 if (isUndefined)
245 *isUndefined = true;
246 } else if (isUndefined) {
247 *isUndefined = result->isUndefined();
248 }
249
250 return result->asReturnedValue();
251}
252
254{
255 // All code that follows must check with watcher before it accesses data members
256 // incase we have been deleted.
258
259 // If there is no engine, we have no way to evaluate anything.
260 // This can happen on destruction.
261 if (!qmlEngine)
262 return false;
263
265
266 QV4::Scope scope(qmlEngine->handle());
267
268 Q_ASSERT(m_qmlScope.valueRef());
270 const bool resultIsDefined = function()->call(
271 scopeObject(), a, types, argc,
272 static_cast<QV4::ExecutionContext *>(m_qmlScope.valueRef()));
273
274 return !capture.catchException(scope) && resultIsDefined;
275}
276
278{
279 if (watcher->wasDeleted())
280 return;
281
283 // Try and find a matching guard
284 while (!guards.isEmpty() && !guards.first()->isConnected(n))
286
288 if (!guards.isEmpty()) {
289 g = guards.takeFirst();
290 g->cancelNotify();
291 Q_ASSERT(g->isConnected(n));
292 } else {
294 g->connect(n);
295 }
296
298}
299
305{
306 if (watcher->wasDeleted())
307 return;
308
310
311 // If c < 0 we won't find any property. We better leave the metaobjects alone in that case.
312 // QQmlListModel expects us _not_ to trigger the creation of dynamic metaobjects from here.
313 if (c >= 0) {
314 const QQmlData *ddata = QQmlData::get(o, /*create=*/false);
315 const QMetaObject *metaObjectForBindable = nullptr;
316 if (auto const propCache = (ddata ? ddata->propertyCache.data() : nullptr)) {
317 Q_ASSERT(propCache->property(c));
318 if (propCache->property(c)->isBindable())
319 metaObjectForBindable = propCache->metaObject();
320 } else {
321 const QMetaObject *m = o->metaObject();
322 if (m->property(c).isBindable())
323 metaObjectForBindable = m;
324 }
325 if (metaObjectForBindable) {
326 captureBindableProperty(o, metaObjectForBindable, c);
327 return;
328 }
329 }
330
331 captureNonBindableProperty(o, n, c, doNotify);
332}
333
335 QObject *o, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *propertyData,
336 bool doNotify)
337{
338 if (watcher->wasDeleted())
339 return;
340
342
343 if (propertyData->isBindable()) {
344 if (const QMetaObject *metaObjectForBindable = propertyCache->metaObject()) {
345 captureBindableProperty(o, metaObjectForBindable, propertyData->coreIndex());
346 return;
347 }
348 }
349
350 captureNonBindableProperty(o, propertyData->notifyIndex(), propertyData->coreIndex(), doNotify);
351}
352
354{
357 while (current) {
358 if (!current->target) {
359 *prev = current->next;
361 current = *prev;
362 } else if (current->target == target && current->propertyIndex == propertyIndex) {
363 return false; // already installed
364 } else {
365 prev = &current->next;
366 current = current->next;
367 }
368 }
369
370 return true;
371}
372
374{
375 // use a unique invalid index to avoid needlessly querying the metaobject for
376 // the correct index of of the translationLanguage property
377 int const invalidIndex = -2;
378 if (expression->needsPropertyChangeTrigger(engine, invalidIndex)) {
379 auto trigger = expression->allocatePropertyChangeTrigger(engine, invalidIndex);
380 trigger->setSource(QQmlEnginePrivate::get(engine)->translationLanguage);
381 }
382}
383
384void QQmlPropertyCapture::captureBindableProperty(
385 QObject *o, const QMetaObject *metaObjectForBindable, int c)
386{
387 // if the property is a QPropery, and we're binding to a QProperty
388 // the automatic capturing process already takes care of everything
390 return;
391
394 QUntypedBindable bindable;
395 void *argv[] = { &bindable };
396 metaObjectForBindable->metacall(o, QMetaObject::BindableProperty, c, argv);
397 bindable.observe(trigger);
398 }
399}
400
401void QQmlPropertyCapture::captureNonBindableProperty(QObject *o, int n, int c, bool doNotify)
402{
403 if (n == -1) {
404 if (!errorString) {
406 QString preamble = QLatin1String("QQmlExpression: Expression ") +
408 QLatin1String(" depends on non-NOTIFYable properties:");
409 errorString->append(preamble);
410 }
411
412 const QMetaProperty metaProp = o->metaObject()->property(c);
414 QString::fromUtf8(o->metaObject()->className()) +
415 QLatin1String("::") +
416 QString::fromUtf8(metaProp.name());
417 errorString->append(error);
418 } else {
419
420 // Try and find a matching guard
421 while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
423
425 if (!guards.isEmpty()) {
426 g = guards.takeFirst();
427 g->cancelNotify();
428 Q_ASSERT(g->isConnected(o, n));
429 } else {
431 g->connect(o, n, engine, doNotify);
432 }
433
435 }
436}
437
439{
441
442 if (m_error)
443 return m_error->error();
444 else
445 return QQmlError();
446}
447
454
457 const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scopeObject,
458 const QString &code, const QString &filename, quint16 line)
459{
460 QQmlEngine *engine = ctxt->engine();
462
464 QV4::Scope scope(v4);
465
467 QV4::Script script(v4, qmlContext, /*parse as QML binding*/true, code, filename, line);
469 script.parse();
470 if (!v4->hasException)
471 result = script.run();
472 if (v4->hasException) {
474 if (error.description().isEmpty())
475 error.setDescription(QLatin1String("Exception occurred during function evaluation"));
476 if (error.line() == -1)
477 error.setLine(line);
478 if (error.url().isEmpty())
479 error.setUrl(QUrl::fromLocalFile(filename));
480 error.setObject(scopeObject);
481 ep->warning(error);
482 return QV4::Encode::undefined();
483 }
484 return result->asReturnedValue();
485}
486
488 const QQmlRefPointer<QQmlContextData> &ctxt, QObject *qmlScope, const QString &code,
489 const QString &filename, quint16 line)
490{
491 QQmlEngine *engine = ctxt->engine();
493
495 QV4::Scope scope(v4);
496
498 QV4::Script script(v4, qmlContext, /*parse as QML binding*/true, code, filename, line);
499 script.parse();
500 if (v4->hasException) {
502 error->catchJavaScriptException(v4);
503 error->setErrorObject(qmlScope);
504 if (!error->addError(ep))
505 ep->warning(error->error());
506 return;
507 }
509}
510
512{
513 if (!qmlContext || !f)
514 return;
515 m_qmlScope.set(qmlContext->engine(), *qmlContext);
516 m_v4Function = f;
517 m_compilationUnit.reset(m_v4Function->executableCompilationUnit());
518}
519
520void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
521{
522 m_compilationUnit = compilationUnit;
523}
524
526 auto This = static_cast<QPropertyChangeTrigger *>(observer);
528}
529
531{
532 if (!target)
533 return {};
534 auto const mo = target->metaObject();
535 if (!mo)
536 return {};
537 return mo->property(propertyIndex);
538}
539
541{
542 auto trigger = QQmlEnginePrivate::get(engine())->qPropertyTriggerPool.New( this );
543 trigger->target = target;
544 trigger->propertyIndex = propertyIndex;
545 auto oldHead = qpropertyChangeTriggers;
546 trigger->next = oldHead;
547 qpropertyChangeTriggers = trigger;
548 return trigger;
549}
550
556
558{
559 QQmlJavaScriptExpression *expression =
560 static_cast<QQmlJavaScriptExpressionGuard *>(e)->expression;
561
562 expression->expressionChanged();
563}
564
void setTag(Tag t)
void copyAndClearPrepend(QForwardFieldList< N, nextMember, OtherTag > &)
bool isEmpty() const
N * first() const
QV4::ExecutionEngine * handle() const
Definition qjsengine.h:298
\inmodule QtCore
\inmodule QtCore
Definition qmetatype.h:341
\inmodule QtCore
Definition qobject.h:103
void setSource(const Property &property)
Definition qproperty.h:267
void addExpression(QQmlJavaScriptExpression *expression)
QQmlEngine * engine() const
Return the context's QQmlEngine, or \nullptr if the context has no QQmlEngine or the QQmlEngine was d...
static QQmlData * get(QObjectPrivate *priv, bool create)
Definition qqmldata_p.h:199
bool addError(QQmlEnginePrivate *)
void setErrorObject(QObject *object)
void catchJavaScriptException(QV4::ExecutionEngine *engine)
void setErrorLocation(const QQmlSourceLocation &sourceLocation)
const QQmlError & error() const
void setErrorDescription(const QString &description)
QQmlPropertyCapture * propertyCapture
void warning(const QQmlError &)
static QQmlEnginePrivate * get(QQmlEngine *e)
QQmlDelayedError * erroredBindings
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
void setObject(QObject *)
Sets the nearest object where this error occurred.
void setColumn(int)
Sets the error column number.
void setLine(int)
Sets the error line number.
void setDescription(const QString &)
Sets the error description.
void setUrl(const QUrl &)
Sets the url for the file that caused this error.
bool catchException(const QV4::Scope &scope) const
QQmlJavaScriptExpressionCapture(QQmlJavaScriptExpression *expression, QQmlEngine *engine)
static QQmlJavaScriptExpressionGuard * New(QQmlJavaScriptExpression *e, QQmlEngine *engine)
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f)
void setCompilationUnit(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit)
bool needsPropertyChangeTrigger(QObject *target, int propertyIndex)
QV4::ReturnedValue evaluate(bool *isUndefined)
virtual QString expressionIdentifier() const
void createQmlBinding(const QQmlRefPointer< QQmlContextData > &ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line)
QForwardFieldList< QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next, GuardTag > activeGuards
void setContext(const QQmlRefPointer< QQmlContextData > &context)
QQmlRefPointer< QQmlContextData > context() const
static QV4::ReturnedValue evalFunction(const QQmlRefPointer< QQmlContextData > &ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line)
QQmlError error(QQmlEngine *) const
virtual QQmlSourceLocation sourceLocation() const
QBiPointer< QObject, DeleteWatcher > m_scopeObject
virtual void expressionChanged()=0
QPropertyChangeTrigger * allocatePropertyChangeTrigger(QObject *target, int propertyIndex)
virtual bool mustCaptureBindableProperty() const
QTaggedPointer< QQmlDelayedError, Tag > m_error
void cancelNotify()
Cancel any notifies that are in progress.
const QMetaObject * metaObject() const
QQmlJavaScriptExpression * expression
void captureProperty(QQmlNotifier *)
QForwardFieldList< QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next > guards
QQmlJavaScriptExpression::DeleteWatcher * watcher
T * data() const
void reset(T *t=nullptr)
static void Delete(T *)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
T * data() const noexcept
\inmodule QtCore
Definition qproperty.h:679
void observe(QPropertyObserver *observer) const
Definition qproperty.h:723
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
Value * valueRef() const
void set(ExecutionEngine *engine, const Value &value)
auto mo
[7]
Combined button and popup list for selecting options.
quint64 ReturnedValue
static void * context
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static bool doNotify(QObject *, QEvent *)
DBusConnection const char DBusError * error
static QDBusError::ErrorType get(const char *name)
#define qWarning
Definition qlogging.h:166
GLsizei const GLfloat * v
[13]
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLenum target
GLboolean GLboolean g
GLfloat n
const GLubyte * c
GLuint64EXT * result
[6]
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:80
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:48
unsigned int uint
Definition qtypes.h:34
class Preamble preamble
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
engine evaluate("var myObject = new MyObject()")
[8]
QJSEngine engine
[0]
\inmodule QtCore
QPointer< QObject > target
QQmlJavaScriptExpression * m_expression
static void trigger(QPropertyObserver *, QUntypedPropertyData *)
int argc() const
StaticValue thisObject
Value * argValues()
static constexpr ReturnedValue undefined()
ExecutionContext * rootContext() const
QQmlError catchExceptionAsQmlError()
ReturnedValue catchException(StackTrace *trace=nullptr)
QV4::ExecutableCompilationUnit * executableCompilationUnit() const
QQmlSourceLocation sourceLocation() const
bool call(QObject *thisObject, void **a, const QMetaType *types, int argc, ExecutionContext *context)
static ReturnedValue wrap(ExecutionEngine *engine, QObject *object)
static Heap::QmlContext * create(QV4::ExecutionContext *parent, QQmlRefPointer< QQmlContextData > context, QObject *scopeObject)
bool hasException() const
ExecutionEngine * engine
ReturnedValue run(const QV4::Value *thisObject=nullptr)
void parse()
Definition qv4script.cpp:44
QV4::WriteBarrier::Pointer< Function > vmFunction
Definition qv4script_p.h:60
const Value & asValue() const
bool isNullOrUndefined() const
TriggerList * next