1// Copyright (C) 2019 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 QV4STATICVALUE_P_H
4#define QV4STATICVALUE_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 <qjsnumbercoercion.h>
18
19#include <QtCore/private/qnumeric_p.h>
20#include <private/qtqmlglobal_p.h>
21
22#include <cstring>
23
24#ifdef QT_NO_DEBUG
25#define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE
26#else
27#define QV4_NEARLY_ALWAYS_INLINE inline
28#endif
29
30QT_BEGIN_NAMESPACE
31
32namespace QV4 {
33
34// ReturnedValue is used to return values from runtime methods
35// the type has to be a primitive type (no struct or union), so that the compiler
36// will return it in a register on all platforms.
37// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm
38typedef quint64 ReturnedValue;
39
40namespace Heap {
41struct Base;
42}
43
44struct StaticValue
45{
46 using HeapBasePtr = Heap::Base *;
47
48 StaticValue() = default;
49 constexpr StaticValue(quint64 val) : _val(val) {}
50
51 StaticValue &operator=(ReturnedValue v)
52 {
53 _val = v;
54 return *this;
55 }
56
57 template<typename Value>
58 StaticValue &operator=(const Value &);
59
60 template<typename Value>
61 const Value &asValue() const;
62
63 template<typename Value>
64 Value &asValue();
65
66 /*
67 We use 8 bytes for a value. In order to store all possible values we employ a variant of NaN
68 boxing. A "special" Double is indicated by a number that has the 11 exponent bits set to 1.
69 Those can be NaN, positive or negative infinity. We only store one variant of NaN: The sign
70 bit has to be off and the bit after the exponent ("quiet bit") has to be on. However, since
71 the exponent bits are enough to identify special doubles, we can use a different bit as
72 discriminator to tell us how the rest of the bits (including quiet and sign) are to be
73 interpreted. This bit is bit 48. If set, we have an unmanaged value, which includes the
74 special doubles and various other values. If unset, we have a managed value, and all of the
75 other bits can be used to assemble a pointer.
76
77 On 32bit systems the pointer can just live in the lower 4 bytes. On 64 bit systems the lower
78 48 bits can be used for verbatim pointer bits. However, since all our heap objects are
79 aligned to 32 bytes, we can use the 5 least significant bits of the pointer to store, e.g.
80 pointer tags on android. The same holds for the 3 bits between the double exponent and
81 bit 48.
82
83 With that out of the way, we can use the other bits to store different values.
84
85 We xor Doubles with (0x7ff48000 << 32). That has the effect that any double with all the
86 exponent bits set to 0 is one of our special doubles. Those special doubles then get the
87 other two bits in the mask (Special and Number) set to 1, as they cannot have 1s in those
88 positions to begin with.
89
90 We dedicate further bits to integer-convertible and bool-or-int. With those bits we can
91 describe all values we need to store.
92
93 Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
94
95 Specific bit-sequences:
96 0 = always 0
97 1 = always 1
98 x = stored value
99 y = stored value, shifted to different position
100 a = xor-ed bits, where at least one bit is set
101 b = xor-ed bits
102
103 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
104 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
105 ------------------------------------------------------------------------+--------------
106 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
107 y0000000 0000yyy0 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxyyyyy | Managed (heap pointer)
108 00000000 00001101 10000000 00000000 00000000 00000000 00000000 00000000 | NaN
109 00000000 00000101 10000000 00000000 00000000 00000000 00000000 00000000 | +Inf
110 10000000 00000101 10000000 00000000 00000000 00000000 00000000 00000000 | -Inf
111 xaaaaaaa aaaaxbxb bxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
112 00000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
113 00000000 00000011 00000000 00000000 00000000 00000000 00000000 00000000 | Null
114 00000000 00000011 10000000 00000000 00000000 00000000 00000000 0000000x | Bool
115 00000000 00000011 11000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
116 ^ ^^^ ^^
117 | ||| ||
118 | ||| |+-> Number
119 | ||| +--> Int or Bool
120 | ||+----> Unmanaged
121 | |+-----> Integer compatible
122 | +------> Special double
123 +--------------------> Double sign, also used for special doubles
124 */
125
126 quint64 _val;
127
128 QV4_NEARLY_ALWAYS_INLINE constexpr quint64 &rawValueRef() { return _val; }
129 QV4_NEARLY_ALWAYS_INLINE constexpr quint64 rawValue() const { return _val; }
130 QV4_NEARLY_ALWAYS_INLINE constexpr void setRawValue(quint64 raw) { _val = raw; }
131
132#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
133 static inline int valueOffset() { return 0; }
134 static inline int tagOffset() { return 4; }
135#else // !Q_LITTLE_ENDIAN
136 static inline int valueOffset() { return 4; }
137 static inline int tagOffset() { return 0; }
138#endif
139 static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << Tag_Shift | value; }
140 QV4_NEARLY_ALWAYS_INLINE constexpr void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << Tag_Shift | value; }
141 QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
142 QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> Tag_Shift; }
143 QV4_NEARLY_ALWAYS_INLINE constexpr void setTag(quint32 tag) { setTagValue(tag, value: value()); }
144
145 QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const
146 {
147 return int(value());
148 }
149 QV4_NEARLY_ALWAYS_INLINE constexpr void setInt_32(int i)
150 {
151 setTagValue(tag: quint32(QuickType::Integer), value: quint32(i));
152 }
153 QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
154
155 QV4_NEARLY_ALWAYS_INLINE constexpr void setEmpty()
156 {
157 setTagValue(tag: quint32(QuickType::Empty), value: 0);
158 }
159
160 enum class TagBit {
161 // s: sign bit
162 // e: double exponent bit
163 // u: upper 3 bits if managed
164 // m: bit 48, denotes "unmanaged" if 1
165 // p: significant pointer bits (some re-used for non-managed)
166 // seeeeeeeeeeeuuumpppp
167 SpecialNegative = 0b10000000000000000000 << 12,
168 SpecialQNaN = 0b00000000000010000000 << 12,
169 Special = 0b00000000000001000000 << 12,
170 IntCompat = 0b00000000000000100000 << 12,
171 Unmanaged = 0b00000000000000010000 << 12,
172 IntOrBool = 0b00000000000000001000 << 12,
173 Number = 0b00000000000000000100 << 12,
174 };
175
176 static inline constexpr quint64 tagBitMask(TagBit bit) { return quint64(bit) << Tag_Shift; }
177
178 enum Type {
179 // Managed, Double and undefined are not directly encoded
180 Managed_Type = 0,
181 Double_Type = 1,
182 Undefined_Type = 2,
183
184 Empty_Type = quint32(TagBit::Unmanaged),
185 Null_Type = Empty_Type | quint32(TagBit::IntCompat),
186 Boolean_Type = Null_Type | quint32(TagBit::IntOrBool),
187 Integer_Type = Boolean_Type | quint32(TagBit::Number)
188 };
189
190 enum {
191 Tag_Shift = 32,
192
193 IsIntegerConvertible_Shift = 48,
194 IsIntegerConvertible_Value = 3, // Unmanaged | IntCompat after shifting
195
196 IsIntegerOrBool_Shift = 47,
197 IsIntegerOrBool_Value = 7, // Unmanaged | IntCompat | IntOrBool after shifting
198 };
199
200 static_assert(IsIntegerConvertible_Value ==
201 (quint32(TagBit::IntCompat) | quint32(TagBit::Unmanaged))
202 >> (IsIntegerConvertible_Shift - Tag_Shift));
203
204 static_assert(IsIntegerOrBool_Value ==
205 (quint32(TagBit::IntOrBool) | quint32(TagBit::IntCompat) | quint32(TagBit::Unmanaged))
206 >> (IsIntegerOrBool_Shift - Tag_Shift));
207
208 static constexpr quint64 ExponentMask = 0b0111111111110000ull << 48;
209
210 static constexpr quint64 Top1Mask = 0b1000000000000000ull << 48;
211 static constexpr quint64 Upper3Mask = 0b0000000000001110ull << 48;
212 static constexpr quint64 Lower5Mask = 0b0000000000011111ull;
213
214 static constexpr quint64 ManagedMask = ExponentMask | quint64(TagBit::Unmanaged) << Tag_Shift;
215 static constexpr quint64 DoubleMask = ManagedMask | quint64(TagBit::Special) << Tag_Shift;
216 static constexpr quint64 NumberMask = ManagedMask | quint64(TagBit::Number) << Tag_Shift;
217 static constexpr quint64 IntOrBoolMask = ManagedMask | quint64(TagBit::IntOrBool) << Tag_Shift;
218 static constexpr quint64 IntCompatMask = ManagedMask | quint64(TagBit::IntCompat) << Tag_Shift;
219
220 static constexpr quint64 EncodeMask = DoubleMask | NumberMask;
221
222 static constexpr quint64 DoubleDiscriminator
223 = ((quint64(TagBit::Unmanaged) | quint64(TagBit::Special)) << Tag_Shift);
224 static constexpr quint64 NumberDiscriminator
225 = ((quint64(TagBit::Unmanaged) | quint64(TagBit::Number)) << Tag_Shift);
226
227 // Things we can immediately determine by just looking at the upper 4 bytes.
228 enum class QuickType : quint32 {
229 // Managed takes precedence over all others. That is, other bits may be set if it's managed.
230 // However, since all others include the Unmanaged bit, we can still check them with simple
231 // equality operations.
232 Managed = Managed_Type,
233
234 Empty = Empty_Type,
235 Null = Null_Type,
236 Boolean = Boolean_Type,
237 Integer = Integer_Type,
238
239 PlusInf = quint32(TagBit::Number) | quint32(TagBit::Special) | quint32(TagBit::Unmanaged),
240 MinusInf = PlusInf | quint32(TagBit::SpecialNegative),
241 NaN = PlusInf | quint32(TagBit::SpecialQNaN),
242 MinusNaN = NaN | quint32(TagBit::SpecialNegative), // Can happen with UMinus on NaN
243 // All other values are doubles
244 };
245
246 // Aliases for easier porting. Remove those when possible
247 using ValueTypeInternal = QuickType;
248 enum {
249 QT_Empty = Empty_Type,
250 QT_Null = Null_Type,
251 QT_Bool = Boolean_Type,
252 QT_Int = Integer_Type,
253 QuickType_Shift = Tag_Shift,
254 };
255
256 inline Type type() const
257 {
258 const quint64 masked = _val & DoubleMask;
259 if (masked >= DoubleDiscriminator)
260 return Double_Type;
261
262 // Any bit set in the exponent would have been caught above, as well as both bits being set.
263 // None of them being set as well as only Special being set means "managed".
264 // Only Unmanaged being set means "unmanaged". That's all remaining options.
265 if (masked != tagBitMask(bit: TagBit::Unmanaged)) {
266 Q_ASSERT((_val & tagBitMask(TagBit::Unmanaged)) == 0);
267 return isUndefined() ? Undefined_Type : Managed_Type;
268 }
269
270 const Type ret = Type(tag());
271 Q_ASSERT(
272 ret == Empty_Type ||
273 ret == Null_Type ||
274 ret == Boolean_Type ||
275 ret == Integer_Type);
276 return ret;
277 }
278
279 inline quint64 quickType() const { return (_val >> QuickType_Shift); }
280
281 // used internally in property
282 inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); }
283 inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); }
284 inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); }
285 inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); }
286 inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
287 inline bool isUndefined() const { return _val == 0; }
288
289 inline bool isDouble() const
290 {
291 // If any of the flipped exponent bits are 1, it's a regular double, and the masked tag is
292 // larger than Unmanaged | Special.
293 //
294 // If all (flipped) exponent bits are 0:
295 // 1. If Unmanaged bit is 0, it's managed
296 // 2. If the Unmanaged bit it is 1, and the Special bit is 0, it's not a special double
297 // 3. If both are 1, it is a special double and the masked tag equals Unmanaged | Special.
298
299 return (_val & DoubleMask) >= DoubleDiscriminator;
300 }
301
302 inline bool isNumber() const
303 {
304 // If any of the flipped exponent bits are 1, it's a regular double, and the masked tag is
305 // larger than Unmanaged | Number.
306 //
307 // If all (flipped) exponent bits are 0:
308 // 1. If Unmanaged bit is 0, it's managed
309 // 2. If the Unmanaged bit it is 1, and the Number bit is 0, it's not number
310 // 3. If both are 1, it is a number and masked tag equals Unmanaged | Number.
311
312 return (_val & NumberMask) >= NumberDiscriminator;
313 }
314
315 inline bool isManagedOrUndefined() const { return (_val & ManagedMask) == 0; }
316
317 // If any other bit is set in addition to the managed mask, it's not undefined.
318 inline bool isManaged() const
319 {
320 return isManagedOrUndefined() && !isUndefined();
321 }
322
323 inline bool isIntOrBool() const
324 {
325 // It's an int or bool if all the exponent bits are 0,
326 // and the "int or bool" bit as well as the "umanaged" bit are set,
327 return (_val >> IsIntegerOrBool_Shift) == IsIntegerOrBool_Value;
328 }
329
330 inline bool integerCompatible() const {
331 Q_ASSERT(!isEmpty());
332 return (_val >> IsIntegerConvertible_Shift) == IsIntegerConvertible_Value;
333 }
334
335 static inline bool integerCompatible(StaticValue a, StaticValue b) {
336 return a.integerCompatible() && b.integerCompatible();
337 }
338
339 static inline bool bothDouble(StaticValue a, StaticValue b) {
340 return a.isDouble() && b.isDouble();
341 }
342
343 inline bool isNaN() const
344 {
345 switch (QuickType(tag())) {
346 case QuickType::NaN:
347 case QuickType::MinusNaN:
348 return true;
349 default:
350 return false;
351 }
352 }
353
354 inline bool isPositiveInt() const {
355 return isInteger() && int_32() >= 0;
356 }
357
358 QV4_NEARLY_ALWAYS_INLINE double doubleValue() const {
359 Q_ASSERT(isDouble());
360 double d;
361 const quint64 unmasked = _val ^ EncodeMask;
362 memcpy(dest: &d, src: &unmasked, n: 8);
363 return d;
364 }
365
366 QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) {
367 if (qt_is_nan(d)) {
368 // We cannot store just any NaN. It has to be a NaN with only the quiet bit
369 // set in the upper bits of the mantissa and the sign bit off.
370 // qt_qnan() happens to produce such a thing via std::numeric_limits,
371 // but this is actually not guaranteed. Therefore, we make our own.
372 _val = (quint64(QuickType::NaN) << Tag_Shift);
373 Q_ASSERT(isNaN());
374 } else {
375 memcpy(dest: &_val, src: &d, n: 8);
376 _val ^= EncodeMask;
377 }
378
379 Q_ASSERT(isDouble());
380 }
381
382 inline bool isInt32() {
383 if (tag() == quint32(QuickType::Integer))
384 return true;
385 if (isDouble()) {
386 double d = doubleValue();
387 if (isInt32(d)) {
388 setInt_32(int(d));
389 return true;
390 }
391 }
392 return false;
393 }
394
395 QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
396 int i = QJSNumberCoercion::toInteger(d);
397 return (i == d && !(d == 0 && std::signbit(x: d)));
398 }
399
400 double asDouble() const {
401 if (tag() == quint32(QuickType::Integer))
402 return int_32();
403 return doubleValue();
404 }
405
406 bool booleanValue() const {
407 return int_32();
408 }
409
410 int integerValue() const {
411 return int_32();
412 }
413
414 inline bool tryIntegerConversion() {
415 bool b = integerCompatible();
416 if (b)
417 setTagValue(tag: quint32(QuickType::Integer), value: value());
418 return b;
419 }
420
421 bool toBoolean() const {
422 if (integerCompatible())
423 return static_cast<bool>(int_32());
424
425 if (isManagedOrUndefined())
426 return false;
427
428 // double
429 const double d = doubleValue();
430 return d && !std::isnan(x: d);
431 }
432
433 inline int toInt32() const
434 {
435 switch (type()) {
436 case Null_Type:
437 case Boolean_Type:
438 case Integer_Type:
439 return int_32();
440 case Double_Type:
441 return QJSNumberCoercion::toInteger(d: doubleValue());
442 case Empty_Type:
443 case Undefined_Type:
444 case Managed_Type:
445 return 0; // Coercion of NaN to int, results in 0;
446 }
447
448 Q_UNREACHABLE_RETURN(0);
449 }
450
451 ReturnedValue *data_ptr() { return &_val; }
452 constexpr ReturnedValue asReturnedValue() const { return _val; }
453 constexpr static StaticValue fromReturnedValue(ReturnedValue val) { return {val}; }
454
455 inline static constexpr StaticValue emptyValue() { return { tagValue(tag: quint32(QuickType::Empty), value: 0) }; }
456 static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(tag: quint32(QuickType::Boolean), value: b) }; }
457 static inline constexpr StaticValue fromInt32(int i) { return { tagValue(tag: quint32(QuickType::Integer), value: quint32(i)) }; }
458 inline static constexpr StaticValue undefinedValue() { return { 0 }; }
459 static inline constexpr StaticValue nullValue() { return { tagValue(tag: quint32(QuickType::Null), value: 0) }; }
460
461 static inline StaticValue fromDouble(double d)
462 {
463 StaticValue v;
464 v.setDouble(d);
465 return v;
466 }
467
468 static inline StaticValue fromUInt32(uint i)
469 {
470 StaticValue v;
471 if (i < uint(std::numeric_limits<int>::max())) {
472 v.setTagValue(tag: quint32(QuickType::Integer), value: i);
473 } else {
474 v.setDouble(i);
475 }
476 return v;
477 }
478
479 static double toInteger(double d)
480 {
481 if (std::isnan(x: d))
482 return +0;
483 if (!d || std::isinf(x: d))
484 return d;
485 return d >= 0 ? std::floor(x: d) : std::ceil(x: d);
486 }
487
488 static int toInt32(double d)
489 {
490 return QJSNumberCoercion::toInteger(d);
491 }
492
493 static unsigned int toUInt32(double d)
494 {
495 return static_cast<uint>(toInt32(d));
496 }
497
498 // While a value containing a Heap::Base* is not actually static, we still implement
499 // the setting and retrieving of heap pointers here in order to have the encoding
500 // scheme completely in one place.
501
502#if QT_POINTER_SIZE == 8
503
504 // All pointer shifts are from more significant to less significant bits.
505 // When encoding, we shift right by that amount. When decoding, we shift left.
506 // Negative numbers mean shifting the other direction. 0 means no shifting.
507 //
508 // The IA64 and Sparc64 cases are mostly there to demonstrate the idea. Sparc64
509 // and IA64 are not officially supported, but we can expect more platforms with
510 // similar "problems" in the future.
511 enum PointerShift {
512#if 0 && defined(Q_OS_ANDROID) && defined(Q_PROCESSOR_ARM_64)
513 // We used to assume that Android on arm64 uses the top byte to store pointer tags.
514 // However, at least currently, the pointer tags are only applied on new/malloc and
515 // delete/free, not on mmap() and munmap(). We manage the JS heap directly using
516 // mmap, so we don't have to preserve any tags.
517 //
518 // If this ever changes, here is how to preserve the top byte:
519 // Move it to Upper3 and Lower5.
520 Top1Shift = 0,
521 Upper3Shift = 12,
522 Lower5Shift = 56,
523#elif defined(Q_PROCESSOR_IA64)
524 // On ia64, bits 63-61 in a 64-bit pointer are used to store the virtual region
525 // number. We can move those to Upper3.
526 Top1Shift = 0,
527 Upper3Shift = 12,
528 Lower5Shift = 0,
529#elif defined(Q_PROCESSOR_SPARC_64)
530 // Sparc64 wants to use 52 bits for pointers.
531 // Upper3 can stay where it is, bit48 moves to the top bit.
532 Top1Shift = -15,
533 Upper3Shift = 0,
534 Lower5Shift = 0,
535#elif 0 // TODO: Once we need 5-level page tables, add the appropriate check here.
536 // With 5-level page tables (as possible on linux) we need 57 address bits.
537 // Upper3 can stay where it is, bit48 moves to the top bit, the rest moves to Lower5.
538 Top1Shift = -15,
539 Upper3Shift = 0,
540 Lower5Shift = 52,
541#else
542 Top1Shift = 0,
543 Upper3Shift = 0,
544 Lower5Shift = 0
545#endif
546 };
547
548 template<int Offset, quint64 Mask>
549 static constexpr quint64 movePointerBits(quint64 val)
550 {
551 if constexpr (Offset > 0)
552 return (val & ~Mask) | ((val & Mask) >> Offset);
553 if constexpr (Offset < 0)
554 return (val & ~Mask) | ((val & Mask) << -Offset);
555 return val;
556 }
557
558 template<int Offset, quint64 Mask>
559 static constexpr quint64 storePointerBits(quint64 val)
560 {
561 constexpr quint64 OriginMask = movePointerBits<-Offset, Mask>(Mask);
562 return movePointerBits<Offset, OriginMask>(val);
563 }
564
565 template<int Offset, quint64 Mask>
566 static constexpr quint64 retrievePointerBits(quint64 val)
567 {
568 return movePointerBits<-Offset, Mask>(val);
569 }
570
571 QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
572 {
573 Q_ASSERT(!(_val & ManagedMask));
574
575 // Re-assemble the pointer from its fragments.
576 const quint64 tmp = retrievePointerBits<Top1Shift, Top1Mask>(
577 val: retrievePointerBits<Upper3Shift, Upper3Mask>(
578 val: retrievePointerBits<Lower5Shift, Lower5Mask>(val: _val)));
579
580 HeapBasePtr b;
581 memcpy(dest: &b, src: &tmp, n: 8);
582 return b;
583 }
584 QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b)
585 {
586 quint64 tmp;
587 memcpy(dest: &tmp, src: &b, n: 8);
588
589 // Has to be aligned to 32 bytes
590 Q_ASSERT(!(tmp & Lower5Mask));
591
592 // Encode the pointer.
593 _val = storePointerBits<Top1Shift, Top1Mask>(
594 val: storePointerBits<Upper3Shift, Upper3Mask>(
595 val: storePointerBits<Lower5Shift, Lower5Mask>(val: tmp)));
596 }
597#elif QT_POINTER_SIZE == 4
598 QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const
599 {
600 Q_STATIC_ASSERT(sizeof(HeapBasePtr) == sizeof(quint32));
601 HeapBasePtr b;
602 quint32 v = value();
603 memcpy(&b, &v, 4);
604 return b;
605 }
606 QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b)
607 {
608 quint32 v;
609 memcpy(&v, &b, 4);
610 setTagValue(quint32(QuickType::Managed), v);
611 }
612#else
613# error "unsupported pointer size"
614#endif
615};
616Q_STATIC_ASSERT(std::is_trivial_v<StaticValue>);
617
618struct Encode {
619 static constexpr ReturnedValue undefined() {
620 return StaticValue::undefinedValue().asReturnedValue();
621 }
622 static constexpr ReturnedValue null() {
623 return StaticValue::nullValue().asReturnedValue();
624 }
625
626 explicit constexpr Encode(bool b)
627 : val(StaticValue::fromBoolean(b).asReturnedValue())
628 {
629 }
630 explicit Encode(double d) {
631 val = StaticValue::fromDouble(d).asReturnedValue();
632 }
633 explicit constexpr Encode(int i)
634 : val(StaticValue::fromInt32(i).asReturnedValue())
635 {
636 }
637 explicit Encode(uint i) {
638 val = StaticValue::fromUInt32(i).asReturnedValue();
639 }
640 explicit constexpr Encode(ReturnedValue v)
641 : val(v)
642 {
643 }
644 constexpr Encode(StaticValue v)
645 : val(v.asReturnedValue())
646 {
647 }
648
649 template<typename HeapBase>
650 explicit Encode(HeapBase *o);
651
652 explicit Encode(StaticValue *o) {
653 Q_ASSERT(o);
654 val = o->asReturnedValue();
655 }
656
657 static ReturnedValue smallestNumber(double d) {
658 if (StaticValue::isInt32(d))
659 return Encode(static_cast<int>(d));
660 else
661 return Encode(d);
662 }
663
664 constexpr operator ReturnedValue() const {
665 return val;
666 }
667 quint64 val;
668private:
669 explicit Encode(void *);
670};
671
672}
673
674QT_END_NAMESPACE
675
676#endif // QV4STATICVALUE_P_H
677

source code of qtdeclarative/src/qml/common/qv4staticvalue_p.h