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