1/****************************************************************************
2**
3** Copyright (C) 2019 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 QV4STATICVALUE_P_H
40#define QV4STATICVALUE_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 <QtCore/private/qnumeric_p.h>
54#include <cstring>
55
56#ifdef QT_NO_DEBUG
57#define QV4_NEARLY_ALWAYS_INLINE Q_ALWAYS_INLINE
58#else
59#define QV4_NEARLY_ALWAYS_INLINE inline
60#endif
61
62QT_BEGIN_NAMESPACE
63
64namespace QV4 {
65
66// ReturnedValue is used to return values from runtime methods
67// the type has to be a primitive type (no struct or union), so that the compiler
68// will return it in a register on all platforms.
69// It will be returned in rax on x64, [eax,edx] on x86 and [r0,r1] on arm
70typedef quint64 ReturnedValue;
71
72struct Double {
73 quint64 d;
74
75 Double(double dbl) {
76 memcpy(dest: &d, src: &dbl, n: sizeof(double));
77 }
78
79 int sign() const {
80 return (d >> 63) ? -1 : 1;
81 }
82
83 bool isDenormal() const {
84 return static_cast<int>((d << 1) >> 53) == 0;
85 }
86
87 int exponent() const {
88 return static_cast<int>((d << 1) >> 53) - 1023;
89 }
90
91 quint64 significant() const {
92 quint64 m = (d << 12) >> 12;
93 if (!isDenormal())
94 m |= (static_cast<quint64>(1) << 52);
95 return m;
96 }
97
98 static int toInt32(double d) {
99 int i = static_cast<int>(d);
100 if (i == d)
101 return i;
102 return Double(d).toInt32();
103 }
104
105 int toInt32() {
106 int e = exponent() - 52;
107 if (e < 0) {
108 if (e <= -53)
109 return 0;
110 return sign() * static_cast<int>(significant() >> -e);
111 } else {
112 if (e > 31)
113 return 0;
114 return sign() * (static_cast<int>(significant()) << e);
115 }
116 }
117};
118
119struct StaticValue
120{
121 StaticValue() = default;
122 constexpr StaticValue(quint64 val) : _val(val) {}
123
124 StaticValue &operator=(ReturnedValue v)
125 {
126 _val = v;
127 return *this;
128 }
129
130 template<typename Value>
131 StaticValue &operator=(const Value &);
132
133 template<typename Value>
134 const Value &asValue() const;
135
136 template<typename Value>
137 Value &asValue();
138
139 /*
140 We use 8 bytes for a value and a different variant of NaN boxing. A Double
141 NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
142 signalling NaN it is the top 14 bits. The other values are usually set to 0 by the
143 processor, and are thus free for us to store other data. We keep pointers in there for
144 managed objects, and encode the other types using the free space given to use by the unused
145 bits for NaN values. This also works for pointers on 64 bit systems, as they all currently
146 only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for
147 pointers.)
148
149 We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
150 get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between
151 managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave
152 the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is
153 set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are
154 used to encode Null/Int/Bool.
155
156 Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr.
157
158 Specific bit-sequences:
159 0 = always 0
160 1 = always 1
161 x = stored value
162 a,b,c,d = specific bit values, see notes
163
164 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
165 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
166 ------------------------------------------------------------------------+--------------
167 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
168 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
169 a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
170 dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
171 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
172 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null
173 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool
174 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
175
176 Notes:
177 - a: xor-ed signbit, always 1 for NaN
178 - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value
179 - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0
180 - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++
181 and JS
182 - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0
183 - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1,
184 so: (val >> (64-15)) == 1
185 - Null, Bool, and Int have bit 48 set, indicating integer-convertible
186 - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where
187 any non double results in a NaN
188 - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to
189 63) are zero. No need to shift.
190 */
191
192 quint64 _val;
193
194 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; }
195 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; }
196 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; }
197
198#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
199 static inline int valueOffset() { return 0; }
200 static inline int tagOffset() { return 4; }
201#else // !Q_LITTLE_ENDIAN
202 static inline int valueOffset() { return 4; }
203 static inline int tagOffset() { return 0; }
204#endif
205 static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; }
206 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
207 QV4_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
208 QV4_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; }
209 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value: value()); }
210
211 QV4_NEARLY_ALWAYS_INLINE constexpr int int_32() const
212 {
213 return int(value());
214 }
215 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i)
216 {
217 setTagValue(tag: quint32(ValueTypeInternal::Integer), value: quint32(i));
218 }
219 QV4_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
220
221 QV4_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty()
222 {
223 setTagValue(tag: quint32(ValueTypeInternal::Empty), value: 0);
224 }
225
226 // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
227 // and use negative numbers here
228 enum QuickType {
229 QT_ManagedOrUndefined = 0,
230 QT_ManagedOrUndefined1 = 1,
231 QT_ManagedOrUndefined2 = 2,
232 QT_ManagedOrUndefined3 = 3,
233 QT_Empty = 4,
234 QT_Null = 5,
235 QT_Bool = 6,
236 QT_Int = 7
237 // all other values are doubles
238 };
239
240 enum Type {
241 Undefined_Type = 0,
242 Managed_Type = 1,
243 Empty_Type = 4,
244 Null_Type = 5,
245 Boolean_Type = 6,
246 Integer_Type = 7,
247 Double_Type = 8
248 };
249
250 inline Type type() const {
251 int t = quickType();
252 if (t < QT_Empty)
253 return _val ? Managed_Type : Undefined_Type;
254 if (t > QT_Int)
255 return Double_Type;
256 return static_cast<Type>(t);
257 }
258
259 // Shared between 32-bit and 64-bit encoding
260 enum {
261 Tag_Shift = 32
262 };
263
264 // Used only by 64-bit encoding
265 static const quint64 NaNEncodeMask = 0xfffc000000000000ull;
266 enum {
267 IsDouble_Shift = 64-14,
268 IsManagedOrUndefined_Shift = 64-15,
269 IsIntegerConvertible_Shift = 64-15,
270 IsIntegerOrBool_Shift = 64-16,
271 QuickType_Shift = 64 - 17,
272 IsPositiveIntShift = 31
273 };
274
275 static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
276
277 enum class ValueTypeInternal_64 {
278 Empty = Immediate_Mask_64 | 0,
279 Null = Immediate_Mask_64 | 0x08000u,
280 Boolean = Immediate_Mask_64 | 0x10000u,
281 Integer = Immediate_Mask_64 | 0x18000u
282 };
283
284 // Used only by 32-bit encoding
285 enum Masks {
286 SilentNaNBit = 0x00040000,
287 NotDouble_Mask = 0x7ffa0000,
288 };
289 static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit;
290
291 enum class ValueTypeInternal_32 {
292 Empty = Immediate_Mask_32 | 0,
293 Null = Immediate_Mask_32 | 0x08000u,
294 Boolean = Immediate_Mask_32 | 0x10000u,
295 Integer = Immediate_Mask_32 | 0x18000u
296 };
297
298 enum {
299 Managed_Type_Internal = 0
300 };
301
302 using ValueTypeInternal = ValueTypeInternal_64;
303
304 enum {
305 NaN_Mask = 0x7ff80000,
306 };
307
308 inline quint64 quickType() const { return (_val >> QuickType_Shift); }
309
310 // used internally in property
311 inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); }
312 inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); }
313 inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); }
314 inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); }
315 inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
316 inline bool isNumber() const { return quickType() >= QT_Int; }
317
318 inline bool isUndefined() const { return _val == 0; }
319 inline bool isDouble() const { return (_val >> IsDouble_Shift); }
320 inline bool isManaged() const
321 {
322#if QT_POINTER_SIZE == 4
323 return value() && tag() == Managed_Type_Internal;
324#else
325 return _val && ((_val >> IsManagedOrUndefined_Shift) == 0);
326#endif
327 }
328 inline bool isManagedOrUndefined() const
329 {
330#if QT_POINTER_SIZE == 4
331 return tag() == Managed_Type_Internal;
332#else
333 return ((_val >> IsManagedOrUndefined_Shift) == 0);
334#endif
335 }
336
337 inline bool isIntOrBool() const {
338 return (_val >> IsIntegerOrBool_Shift) == 3;
339 }
340
341 inline bool integerCompatible() const {
342 Q_ASSERT(!isEmpty());
343 return (_val >> IsIntegerConvertible_Shift) == 1;
344 }
345
346 static inline bool integerCompatible(StaticValue a, StaticValue b) {
347 return a.integerCompatible() && b.integerCompatible();
348 }
349
350 static inline bool bothDouble(StaticValue a, StaticValue b) {
351 return a.isDouble() && b.isDouble();
352 }
353
354 inline bool isNaN() const
355 {
356 return (tag() & 0x7ffc0000 ) == 0x00040000;
357 }
358
359 inline bool isPositiveInt() const {
360#if QT_POINTER_SIZE == 4
361 return isInteger() && int_32() >= 0;
362#else
363 return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
364#endif
365 }
366
367 QV4_NEARLY_ALWAYS_INLINE double doubleValue() const {
368 Q_ASSERT(isDouble());
369 double d;
370 StaticValue v = *this;
371 v._val ^= NaNEncodeMask;
372 memcpy(dest: &d, src: &v._val, n: 8);
373 return d;
374 }
375
376 QV4_NEARLY_ALWAYS_INLINE void setDouble(double d) {
377 if (qt_is_nan(d))
378 d = qt_qnan();
379 memcpy(dest: &_val, src: &d, n: 8);
380 _val ^= NaNEncodeMask;
381 Q_ASSERT(isDouble());
382 }
383
384 inline bool isInt32() {
385 if (tag() == quint32(ValueTypeInternal::Integer))
386 return true;
387 if (isDouble()) {
388 double d = doubleValue();
389 if (isInt32(d)) {
390 setInt_32(int(d));
391 return true;
392 }
393 }
394 return false;
395 }
396
397 QV4_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
398 int i = int(d);
399 return (i == d && !(d == 0 && std::signbit(x: d)));
400 }
401
402 double asDouble() const {
403 if (tag() == quint32(ValueTypeInternal::Integer))
404 return int_32();
405 return doubleValue();
406 }
407
408 bool booleanValue() const {
409 return int_32();
410 }
411
412 int integerValue() const {
413 return int_32();
414 }
415
416 inline bool tryIntegerConversion() {
417 bool b = integerCompatible();
418 if (b)
419 setTagValue(tag: quint32(ValueTypeInternal::Integer), value: value());
420 return b;
421 }
422
423 bool toBoolean() const {
424 if (integerCompatible())
425 return static_cast<bool>(int_32());
426
427 if (isManagedOrUndefined())
428 return false;
429
430 // double
431 const double d = doubleValue();
432 return d && !std::isnan(x: d);
433 }
434
435 inline int toInt32() const
436 {
437 switch (type()) {
438 case Null_Type:
439 case Boolean_Type:
440 case Integer_Type:
441 return int_32();
442 case Double_Type:
443 return Double::toInt32(d: doubleValue());
444 case Empty_Type:
445 case Undefined_Type:
446 case Managed_Type:
447 break;
448 }
449 return Double::toInt32(d: std::numeric_limits<double>::quiet_NaN());
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(tag: quint32(ValueTypeInternal::Empty), value: 0) }; }
457 static inline constexpr StaticValue fromBoolean(bool b) { return { tagValue(tag: quint32(ValueTypeInternal::Boolean), value: b) }; }
458 static inline constexpr StaticValue fromInt32(int i) { return { tagValue(tag: quint32(ValueTypeInternal::Integer), value: quint32(i)) }; }
459 inline static constexpr StaticValue undefinedValue() { return { 0 }; }
460 static inline constexpr StaticValue nullValue() { return { tagValue(tag: quint32(ValueTypeInternal::Null), value: 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(tag: quint32(ValueTypeInternal::Integer), value: i);
474 } else {
475 v.setDouble(i);
476 }
477 return v;
478 }
479
480 static double toInteger(double d)
481 {
482 if (std::isnan(x: d))
483 return +0;
484 if (!d || std::isinf(x: d))
485 return d;
486 return d >= 0 ? std::floor(x: d) : std::ceil(x: d);
487 }
488
489 static int toInt32(double d)
490 {
491 return Double::toInt32(d);
492 }
493
494 static unsigned int toUInt32(double d)
495 {
496 return static_cast<uint>(toInt32(d));
497 }
498};
499Q_STATIC_ASSERT(std::is_trivial<StaticValue>::value);
500
501struct Encode {
502 static constexpr ReturnedValue undefined() {
503 return StaticValue::undefinedValue().asReturnedValue();
504 }
505 static constexpr ReturnedValue null() {
506 return StaticValue::nullValue().asReturnedValue();
507 }
508
509 explicit constexpr Encode(bool b)
510 : val(StaticValue::fromBoolean(b).asReturnedValue())
511 {
512 }
513 explicit Encode(double d) {
514 val = StaticValue::fromDouble(d).asReturnedValue();
515 }
516 explicit constexpr Encode(int i)
517 : val(StaticValue::fromInt32(i).asReturnedValue())
518 {
519 }
520 explicit Encode(uint i) {
521 val = StaticValue::fromUInt32(i).asReturnedValue();
522 }
523 explicit constexpr Encode(ReturnedValue v)
524 : val(v)
525 {
526 }
527 constexpr Encode(StaticValue v)
528 : val(v.asReturnedValue())
529 {
530 }
531
532 template<typename HeapBase>
533 explicit Encode(HeapBase *o);
534
535 explicit Encode(StaticValue *o) {
536 Q_ASSERT(o);
537 val = o->asReturnedValue();
538 }
539
540 static ReturnedValue smallestNumber(double d) {
541 if (StaticValue::isInt32(d))
542 return Encode(static_cast<int>(d));
543 else
544 return Encode(d);
545 }
546
547 constexpr operator ReturnedValue() const {
548 return val;
549 }
550 quint64 val;
551private:
552 explicit Encode(void *);
553};
554
555}
556
557QT_END_NAMESPACE
558
559#endif // QV4STATICVALUE_P_H
560

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