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