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