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 | #ifndef QV4SCOPEDVALUE_P_H |
4 | #define QV4SCOPEDVALUE_P_H |
5 | |
6 | // |
7 | // W A R N I N G |
8 | // ------------- |
9 | // |
10 | // This file is not part of the Qt API. It exists purely as an |
11 | // implementation detail. This header file may change from version to |
12 | // version without notice, or even be removed. |
13 | // |
14 | // We mean it. |
15 | // |
16 | |
17 | #include "qv4engine_p.h" |
18 | #include "qv4value_p.h" |
19 | #include "qv4property_p.h" |
20 | #include "qv4propertykey_p.h" |
21 | |
22 | #ifdef V4_USE_VALGRIND |
23 | #include <valgrind/memcheck.h> |
24 | #endif |
25 | |
26 | QT_BEGIN_NAMESPACE |
27 | |
28 | #define SAVE_JS_STACK(ctx) Value *__jsStack = ctx->engine->jsStackTop |
29 | #define CHECK_JS_STACK(ctx) Q_ASSERT(__jsStack == ctx->engine->jsStackTop) |
30 | |
31 | namespace QV4 { |
32 | |
33 | struct ScopedValue; |
34 | |
35 | inline bool hasExceptionOrIsInterrupted(ExecutionEngine *engine) |
36 | { |
37 | return engine->hasException || engine->isInterrupted.loadRelaxed(); |
38 | } |
39 | |
40 | #define CHECK_EXCEPTION() \ |
41 | do { \ |
42 | if (hasExceptionOrIsInterrupted(scope.engine)) { \ |
43 | return QV4::Encode::undefined(); \ |
44 | } \ |
45 | } while (false) |
46 | |
47 | #define RETURN_UNDEFINED() \ |
48 | return QV4::Encode::undefined() |
49 | |
50 | #define RETURN_RESULT(r) \ |
51 | return QV4::Encode(r) |
52 | |
53 | #define THROW_TYPE_ERROR() \ |
54 | return scope.engine->throwTypeError() |
55 | |
56 | #define THROW_GENERIC_ERROR(str) \ |
57 | return scope.engine->throwError(QString::fromUtf8(str)) |
58 | |
59 | struct Scope { |
60 | explicit Scope(ExecutionContext *ctx) |
61 | : engine(ctx->engine()) |
62 | , mark(engine->jsStackTop) |
63 | { |
64 | } |
65 | |
66 | explicit Scope(ExecutionEngine *e) |
67 | : engine(e) |
68 | , mark(engine->jsStackTop) |
69 | { |
70 | } |
71 | |
72 | explicit Scope(const Managed *m) |
73 | : engine(m->engine()) |
74 | , mark(engine->jsStackTop) |
75 | { |
76 | } |
77 | |
78 | ~Scope() { |
79 | #ifndef QT_NO_DEBUG |
80 | Q_ASSERT(engine->jsStackTop >= mark); |
81 | // Q_ASSERT(engine->currentContext < mark); |
82 | memset(s: mark, c: 0, n: (engine->jsStackTop - mark)*sizeof(Value)); |
83 | #endif |
84 | #ifdef V4_USE_VALGRIND |
85 | VALGRIND_MAKE_MEM_UNDEFINED(mark, (engine->jsStackLimit - mark) * sizeof(Value)); |
86 | #endif |
87 | engine->jsStackTop = mark; |
88 | } |
89 | |
90 | enum AllocMode { |
91 | Undefined, |
92 | Empty, |
93 | /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */ |
94 | Uninitialized |
95 | }; |
96 | |
97 | template <AllocMode mode = Undefined> |
98 | Value *alloc(qint64 nValues) const = delete; // use safeForAllocLength |
99 | |
100 | template <AllocMode mode = Undefined> |
101 | QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const |
102 | { |
103 | Value *ptr = engine->jsAlloca(nValues); |
104 | switch (mode) { |
105 | case Undefined: |
106 | for (int i = 0; i < nValues; ++i) |
107 | ptr[i] = Value::undefinedValue(); |
108 | break; |
109 | case Empty: |
110 | for (int i = 0; i < nValues; ++i) |
111 | ptr[i] = Value::emptyValue(); |
112 | break; |
113 | case Uninitialized: |
114 | break; |
115 | } |
116 | return ptr; |
117 | } |
118 | template <AllocMode mode = Undefined> |
119 | QML_NEARLY_ALWAYS_INLINE Value *alloc() const |
120 | { |
121 | Value *ptr = engine->jsAlloca(nValues: 1); |
122 | switch (mode) { |
123 | case Undefined: |
124 | *ptr = Value::undefinedValue(); |
125 | break; |
126 | case Empty: |
127 | *ptr = Value::emptyValue(); |
128 | break; |
129 | case Uninitialized: |
130 | break; |
131 | } |
132 | return ptr; |
133 | } |
134 | |
135 | bool hasException() const { |
136 | return engine->hasException; |
137 | } |
138 | |
139 | ExecutionEngine *engine; |
140 | Value *mark; |
141 | |
142 | private: |
143 | Q_DISABLE_COPY(Scope) |
144 | }; |
145 | |
146 | struct ScopedValue |
147 | { |
148 | ScopedValue(const ScopedValue &) = default; |
149 | ScopedValue(ScopedValue &&) = default; |
150 | |
151 | ScopedValue(const Scope &scope) |
152 | { |
153 | ptr = scope.alloc<Scope::Uninitialized>(); |
154 | ptr->setRawValue(0); |
155 | } |
156 | |
157 | ScopedValue(const Scope &scope, const Value &v) |
158 | { |
159 | ptr = scope.alloc<Scope::Uninitialized>(); |
160 | *ptr = v; |
161 | } |
162 | |
163 | ScopedValue(const Scope &scope, Heap::Base *o) |
164 | { |
165 | ptr = scope.alloc<Scope::Uninitialized>(); |
166 | ptr->setM(o); |
167 | } |
168 | |
169 | ScopedValue(const Scope &scope, Managed *m) |
170 | { |
171 | ptr = scope.alloc<Scope::Uninitialized>(); |
172 | ptr->setRawValue(m->asReturnedValue()); |
173 | } |
174 | |
175 | ScopedValue(const Scope &scope, const ReturnedValue &v) |
176 | { |
177 | ptr = scope.alloc<Scope::Uninitialized>(); |
178 | ptr->setRawValue(v); |
179 | } |
180 | |
181 | ScopedValue &operator=(const Value &v) { |
182 | *ptr = v; |
183 | return *this; |
184 | } |
185 | |
186 | ScopedValue &operator=(Heap::Base *o) { |
187 | ptr->setM(o); |
188 | return *this; |
189 | } |
190 | |
191 | ScopedValue &operator=(Managed *m) { |
192 | *ptr = *m; |
193 | return *this; |
194 | } |
195 | |
196 | ScopedValue &operator=(const ReturnedValue &v) { |
197 | ptr->setRawValue(v); |
198 | return *this; |
199 | } |
200 | |
201 | ScopedValue &operator=(const ScopedValue &other) { |
202 | *ptr = *other.ptr; |
203 | return *this; |
204 | } |
205 | |
206 | Value *operator->() { |
207 | return ptr; |
208 | } |
209 | |
210 | const Value *operator->() const { |
211 | return ptr; |
212 | } |
213 | |
214 | operator Value *() { return ptr; } |
215 | operator const Value &() const { return *ptr; } |
216 | |
217 | Value *ptr; |
218 | }; |
219 | |
220 | |
221 | struct ScopedPropertyKey |
222 | { |
223 | ScopedPropertyKey(const Scope &scope) |
224 | { |
225 | ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>()); |
226 | *ptr = PropertyKey::invalid(); |
227 | } |
228 | |
229 | ScopedPropertyKey(const Scope &scope, const PropertyKey &v) |
230 | { |
231 | ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>()); |
232 | *ptr = v; |
233 | } |
234 | |
235 | ScopedPropertyKey &operator=(const PropertyKey &other) { |
236 | *ptr = other; |
237 | return *this; |
238 | } |
239 | |
240 | PropertyKey *operator->() { |
241 | return ptr; |
242 | } |
243 | operator PropertyKey() const { |
244 | return *ptr; |
245 | } |
246 | |
247 | bool operator==(const PropertyKey &other) const { |
248 | return *ptr == other; |
249 | } |
250 | bool operator==(const ScopedPropertyKey &other) const { |
251 | return *ptr == *other.ptr; |
252 | } |
253 | bool operator!=(const PropertyKey &other) const { |
254 | return *ptr != other; |
255 | } |
256 | bool operator!=(const ScopedPropertyKey &other) const { |
257 | return *ptr != *other.ptr; |
258 | } |
259 | |
260 | PropertyKey *ptr; |
261 | }; |
262 | |
263 | |
264 | template<typename T> |
265 | struct Scoped |
266 | { |
267 | enum ConvertType { Convert }; |
268 | |
269 | QML_NEARLY_ALWAYS_INLINE void setPointer(const Managed *p) { |
270 | ptr->setM(p ? p->m() : nullptr); |
271 | } |
272 | |
273 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope) |
274 | { |
275 | ptr = scope.alloc<Scope::Undefined>(); |
276 | } |
277 | |
278 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v) |
279 | { |
280 | ptr = scope.alloc<Scope::Uninitialized>(); |
281 | setPointer(v.as<T>()); |
282 | } |
283 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, Heap::Base *o) |
284 | { |
285 | Value v; |
286 | v = o; |
287 | ptr = scope.alloc<Scope::Uninitialized>(); |
288 | setPointer(v.as<T>()); |
289 | } |
290 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ScopedValue &v) |
291 | { |
292 | ptr = scope.alloc<Scope::Uninitialized>(); |
293 | setPointer(v.ptr->as<T>()); |
294 | } |
295 | |
296 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v, ConvertType) |
297 | { |
298 | ptr = scope.alloc<Scope::Uninitialized>(); |
299 | ptr->setRawValue(value_convert<T>(scope.engine, v)); |
300 | } |
301 | |
302 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value *v) |
303 | { |
304 | ptr = scope.alloc<Scope::Uninitialized>(); |
305 | setPointer(v ? v->as<T>() : nullptr); |
306 | } |
307 | |
308 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, T *t) |
309 | { |
310 | ptr = scope.alloc<Scope::Uninitialized>(); |
311 | setPointer(t); |
312 | } |
313 | |
314 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const T *t) |
315 | { |
316 | ptr = scope.alloc<Scope::Uninitialized>(); |
317 | setPointer(t); |
318 | } |
319 | |
320 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, typename T::Data *t) |
321 | { |
322 | ptr = scope.alloc<Scope::Uninitialized>(); |
323 | *ptr = t; |
324 | } |
325 | |
326 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v) |
327 | { |
328 | ptr = scope.alloc<Scope::Uninitialized>(); |
329 | setPointer(QV4::Value::fromReturnedValue(val: v).as<T>()); |
330 | } |
331 | |
332 | QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v, ConvertType) |
333 | { |
334 | ptr = scope.alloc<Scope::Uninitialized>(); |
335 | ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(val: v))); |
336 | } |
337 | |
338 | Scoped<T> &operator=(Heap::Base *o) { |
339 | setPointer(Value::fromHeapObject(m: o).as<T>()); |
340 | return *this; |
341 | } |
342 | Scoped<T> &operator=(typename T::Data *t) { |
343 | *ptr = t; |
344 | return *this; |
345 | } |
346 | Scoped<T> &operator=(const Value &v) { |
347 | setPointer(v.as<T>()); |
348 | return *this; |
349 | } |
350 | Scoped<T> &operator=(Value *v) { |
351 | setPointer(v ? v->as<T>() : nullptr); |
352 | return *this; |
353 | } |
354 | |
355 | Scoped<T> &operator=(const ReturnedValue &v) { |
356 | setPointer(QV4::Value::fromReturnedValue(val: v).as<T>()); |
357 | return *this; |
358 | } |
359 | |
360 | Scoped<T> &operator=(T *t) { |
361 | setPointer(t); |
362 | return *this; |
363 | } |
364 | |
365 | operator T *() { |
366 | return static_cast<T *>(ptr->managed()); |
367 | } |
368 | operator const Value &() const { |
369 | return *ptr; |
370 | } |
371 | |
372 | T *operator->() { |
373 | return getPointer(); |
374 | } |
375 | |
376 | const T *operator->() const { |
377 | return getPointer(); |
378 | } |
379 | |
380 | explicit operator bool() const { |
381 | return ptr->m(); |
382 | } |
383 | |
384 | T *getPointer() { |
385 | return reinterpret_cast<T *>(ptr); |
386 | } |
387 | |
388 | const T *getPointer() const { |
389 | return reinterpret_cast<T *>(ptr); |
390 | } |
391 | |
392 | Value *getRef() { |
393 | return ptr; |
394 | } |
395 | |
396 | QML_NEARLY_ALWAYS_INLINE ReturnedValue asReturnedValue() const { |
397 | return ptr->rawValue(); |
398 | } |
399 | |
400 | Value *ptr; |
401 | }; |
402 | |
403 | inline Value &Value::operator =(const ScopedValue &v) |
404 | { |
405 | _val = v.ptr->rawValue(); |
406 | return *this; |
407 | } |
408 | |
409 | template<typename T> |
410 | inline Value &Value::operator=(const Scoped<T> &t) |
411 | { |
412 | _val = t.ptr->rawValue(); |
413 | return *this; |
414 | } |
415 | |
416 | struct ScopedProperty |
417 | { |
418 | ScopedProperty(Scope &scope) |
419 | { |
420 | property = reinterpret_cast<Property*>(scope.alloc(nValues: int(sizeof(Property) / sizeof(Value)))); |
421 | } |
422 | |
423 | Property *operator->() { return property; } |
424 | operator const Property *() const { return property; } |
425 | operator Property *() { return property; } |
426 | |
427 | Property *property; |
428 | }; |
429 | |
430 | } |
431 | |
432 | QT_END_NAMESPACE |
433 | |
434 | #endif |
435 | |