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#ifndef QQMLJAVASCRIPTEXPRESSION_P_H
41#define QQMLJAVASCRIPTEXPRESSION_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtCore/qglobal.h>
55#include <QtQml/qqmlerror.h>
56#include <private/qqmlengine_p.h>
57
58QT_BEGIN_NAMESPACE
59
60struct QQmlSourceLocation;
61
62class QQmlDelayedError
63{
64public:
65 inline QQmlDelayedError() : nextError(nullptr), prevError(nullptr) {}
66 inline ~QQmlDelayedError() { (void)removeError(); }
67
68 bool addError(QQmlEnginePrivate *);
69
70 Q_REQUIRED_RESULT inline QQmlError removeError() {
71 if (prevError) {
72 if (nextError) nextError->prevError = prevError;
73 *prevError = nextError;
74 nextError = nullptr;
75 prevError = nullptr;
76 }
77 return m_error;
78 }
79
80 inline bool isValid() const { return m_error.isValid(); }
81 inline const QQmlError &error() const { return m_error; }
82 inline void clearError() { m_error = QQmlError(); }
83
84 void setErrorLocation(const QQmlSourceLocation &sourceLocation);
85 void setErrorDescription(const QString &description);
86 void setErrorObject(QObject *object);
87
88 // Call only from catch(...) -- will re-throw if no JS exception
89 void catchJavaScriptException(QV4::ExecutionEngine *engine);
90
91private:
92
93 mutable QQmlError m_error;
94
95 QQmlDelayedError *nextError;
96 QQmlDelayedError **prevError;
97};
98
99class Q_QML_PRIVATE_EXPORT QQmlJavaScriptExpression
100{
101public:
102 QQmlJavaScriptExpression();
103 virtual ~QQmlJavaScriptExpression();
104
105 virtual QString expressionIdentifier() const = 0;
106 virtual void expressionChanged() = 0;
107
108 QV4::ReturnedValue evaluate(bool *isUndefined);
109 QV4::ReturnedValue evaluate(QV4::CallData *callData, bool *isUndefined);
110
111 inline bool notifyOnValueChanged() const;
112
113 void setNotifyOnValueChanged(bool v);
114 void resetNotifyOnValueChanged();
115
116 inline QObject *scopeObject() const;
117 inline void setScopeObject(QObject *v);
118
119 virtual QQmlSourceLocation sourceLocation() const;
120
121 bool isValid() const { return context() != nullptr; }
122
123 QQmlContextData *context() const { return m_context; }
124 void setContext(QQmlContextData *context);
125
126 QV4::Function *function() const;
127
128 virtual void refresh();
129
130 class DeleteWatcher {
131 public:
132 inline DeleteWatcher(QQmlJavaScriptExpression *);
133 inline ~DeleteWatcher();
134 inline bool wasDeleted() const;
135 private:
136 friend class QQmlJavaScriptExpression;
137 QObject *_c;
138 QQmlJavaScriptExpression **_w;
139 QQmlJavaScriptExpression *_s;
140 };
141
142 inline bool hasError() const;
143 inline bool hasDelayedError() const;
144 QQmlError error(QQmlEngine *) const;
145 void clearError();
146 void clearActiveGuards();
147 QQmlDelayedError *delayedError();
148
149 static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope,
150 const QString &code, const QString &filename,
151 quint16 line);
152protected:
153 void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line);
154
155 void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
156 void setCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit);
157
158 // We store some flag bits in the following flag pointers.
159 // activeGuards:flag1 - notifyOnValueChanged
160 // activeGuards:flag2 - useSharedContext
161 QBiPointer<QObject, DeleteWatcher> m_scopeObject;
162 QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards;
163
164 void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); }
165 bool translationsCaptured() const { return m_error.flag(); }
166
167private:
168 friend class QQmlContextData;
169 friend class QQmlPropertyCapture;
170 friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **);
171 friend class QQmlTranslationBinding;
172
173 // m_error:flag1 translationsCapturedDuringEvaluation
174 QFlagPointer<QQmlDelayedError> m_error;
175
176 QQmlContextData *m_context;
177 QQmlJavaScriptExpression **m_prevExpression;
178 QQmlJavaScriptExpression *m_nextExpression;
179
180 QV4::PersistentValue m_qmlScope;
181 QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
182 QV4::Function *m_v4Function;
183};
184
185class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture
186{
187public:
188 QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w)
189 : engine(engine), expression(e), watcher(w), errorString(nullptr) { }
190
191 ~QQmlPropertyCapture() {
192 Q_ASSERT(guards.isEmpty());
193 Q_ASSERT(errorString == nullptr);
194 }
195
196 void captureProperty(QQmlNotifier *);
197 void captureProperty(QObject *, int, int, bool doNotify = true);
198 void captureTranslation() { translationCaptured = true; }
199
200 QQmlEngine *engine;
201 QQmlJavaScriptExpression *expression;
202 QQmlJavaScriptExpression::DeleteWatcher *watcher;
203 QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards;
204 QStringList *errorString;
205 bool translationCaptured = false;
206};
207
208QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e)
209: _c(nullptr), _w(nullptr), _s(e)
210{
211 if (e->m_scopeObject.isT1()) {
212 _w = &_s;
213 _c = e->m_scopeObject.asT1();
214 e->m_scopeObject = this;
215 } else {
216 // Another watcher is already registered
217 _w = &e->m_scopeObject.asT2()->_s;
218 }
219}
220
221QQmlJavaScriptExpression::DeleteWatcher::~DeleteWatcher()
222{
223 Q_ASSERT(*_w == nullptr || (*_w == _s && _s->m_scopeObject.isT2()));
224 if (*_w && _s->m_scopeObject.asT2() == this)
225 _s->m_scopeObject = _c;
226}
227
228bool QQmlJavaScriptExpression::DeleteWatcher::wasDeleted() const
229{
230 return *_w == nullptr;
231}
232
233bool QQmlJavaScriptExpression::notifyOnValueChanged() const
234{
235 return activeGuards.flag();
236}
237
238QObject *QQmlJavaScriptExpression::scopeObject() const
239{
240 if (m_scopeObject.isT1()) return m_scopeObject.asT1();
241 else return m_scopeObject.asT2()->_c;
242}
243
244void QQmlJavaScriptExpression::setScopeObject(QObject *v)
245{
246 if (m_scopeObject.isT1()) m_scopeObject = v;
247 else m_scopeObject.asT2()->_c = v;
248}
249
250bool QQmlJavaScriptExpression::hasError() const
251{
252 return !m_error.isNull() && m_error->isValid();
253}
254
255bool QQmlJavaScriptExpression::hasDelayedError() const
256{
257 return !m_error.isNull();
258}
259
260inline void QQmlJavaScriptExpression::clearError()
261{
262 delete m_error.data();
263 m_error = nullptr;
264}
265
266QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e)
267 : QQmlNotifierEndpoint(QQmlNotifierEndpoint::QQmlJavaScriptExpressionGuard),
268 expression(e), next(nullptr)
269{
270}
271
272QQmlJavaScriptExpressionGuard *
273QQmlJavaScriptExpressionGuard::New(QQmlJavaScriptExpression *e,
274 QQmlEngine *engine)
275{
276 Q_ASSERT(e);
277 return QQmlEnginePrivate::get(engine)->jsExpressionGuardPool.New(e);
278}
279
280void QQmlJavaScriptExpressionGuard::Delete()
281{
282 QRecyclePool<QQmlJavaScriptExpressionGuard>::Delete(this);
283}
284
285
286QT_END_NAMESPACE
287
288#endif // QQMLJAVASCRIPTEXPRESSION_P_H
289