1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL21$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32 ****************************************************************************/
38 #include <QtCore/QString>
39 #include "qv4global_p.h"
40 #include <private/qv4heap_p.h>
42 #ifndef Q_ALWAYS_INLINE
44 # define Q_ALWAYS_INLINE inline __attribute__((always_inline))
45 #elif defined(Q_CC_MSVC)
46 # define Q_ALWAYS_INLINE __forceinline
48 # define Q_ALWAYS_INLINE inline
50 #endif // Q_ALWAYS_INLINE
62 struct Q_QML_PRIVATE_EXPORT Value
65 We use two different ways of encoding JS values. One for 32bit and one for 64bit systems.
67 In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double NaN (actually -qNaN)
68 is indicated by a number that has the top 13 bits set. The other values are usually set to 0 by the
69 processor, and are thus free for us to store other data. We keep pointers in there for managed objects,
70 and encode the other types using the free space given to use by the unused bits for NaN values. This also
71 works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory.
73 On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that
74 will make the number a NaN. The Masks below are used for encoding the other types.
76 On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded
77 with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These
78 can be used, as the highest valid pointer on a 64 bit system is 2^48-1.
80 If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer.
81 This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set).
83 Bit 15-17 is then used to encode other immediates.
88 Q_ALWAYS_INLINE quint64 val() const { return _val; }
89 Q_ALWAYS_INLINE void setVal(quint64 v) { _val = v; }
90 Q_ALWAYS_INLINE void setValue(quint32 v) { setTagValue(tag(), v); }
91 Q_ALWAYS_INLINE void setTag(quint32 t) { setTagValue(t, value()); }
93 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
94 static inline int valueOffset() { return 0; }
95 static inline int tagOffset() { return 4; }
96 Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
97 Q_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); }
98 Q_ALWAYS_INLINE quint32 tag() const { return _val >> 32; }
99 #else // !Q_LITTLE_ENDIAN
100 static inline int valueOffset() { return 4; }
101 static inline int tagOffset() { return 0; }
102 Q_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(value) << 32 | tag; }
103 Q_ALWAYS_INLINE quint32 tag() const { return _val & quint64(~quint32(0)); }
104 Q_ALWAYS_INLINE quint32 value() const { return _val >> 32; }
107 #if QT_POINTER_SIZE == 8
108 Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; memcpy(&b, &_val, 8); return b; }
109 Q_ALWAYS_INLINE void setM(Heap::Base *b) { memcpy(&_val, &b, 8); }
110 #else // QT_POINTER_SIZE == 4
111 Q_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; quint32 v = value(); memcpy(&b, &v, 4); return b; }
112 Q_ALWAYS_INLINE void setM(Heap::Base *b) { quint32 v; memcpy(&v, &b, 4); setValue(v); }
115 Q_ALWAYS_INLINE int int_32() const { int i; quint32 v = value(); memcpy(&i, &v, 4); return i; }
116 Q_ALWAYS_INLINE void setInt_32(int i) { quint32 u; memcpy(&u, &i, 4); setValue(u); }
117 Q_ALWAYS_INLINE uint uint_32() const { return value(); }
119 #if QT_POINTER_SIZE == 4
121 SilentNaNBit = 0x00040000,
122 NaN_Mask = 0x7ff80000,
123 NotDouble_Mask = 0x7ffa0000,
124 Type_Mask = 0xffffc000,
125 Immediate_Mask = NotDouble_Mask | 0x00004000 | SilentNaNBit,
126 IsNullOrUndefined_Mask = Immediate_Mask | 0x08000,
130 Undefined_Type = Immediate_Mask | 0x00000,
131 Null_Type = Immediate_Mask | 0x10000,
132 Boolean_Type = Immediate_Mask | 0x08000,
133 Integer_Type = Immediate_Mask | 0x18000,
134 Managed_Type = NotDouble_Mask | 0x00000 | SilentNaNBit,
135 Empty_Type = NotDouble_Mask | 0x18000 | SilentNaNBit
138 enum ImmediateFlags {
139 ConvertibleToInt = Immediate_Mask | 0x1
142 enum ValueTypeInternal {
143 _Null_Type = Null_Type | ConvertibleToInt,
144 _Boolean_Type = Boolean_Type | ConvertibleToInt,
145 _Integer_Type = Integer_Type | ConvertibleToInt,
149 static const quint64 NaNEncodeMask = 0xffff800000000000ll;
150 static const quint64 IsInt32Mask = 0x0002000000000000ll;
151 static const quint64 IsDoubleMask = 0xfffc000000000000ll;
152 static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask;
153 static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll;
154 static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll;
155 static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask;
158 NaN_Mask = 0x7ff80000,
159 Type_Mask = 0xffff8000,
160 IsDouble_Mask = 0xfffc0000,
161 Immediate_Mask = 0x00018000,
162 IsNullOrUndefined_Mask = 0x00008000,
163 IsNullOrBoolean_Mask = 0x00010000,
167 Undefined_Type = IsNullOrUndefined_Mask,
168 Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask,
169 Boolean_Type = IsNullOrBoolean_Mask,
170 Integer_Type = 0x20000|IsNullOrBoolean_Mask,
172 Empty_Type = Undefined_Type | 0x4000
175 IsDouble_Shift = 64-14,
176 IsNumber_Shift = 64-15,
177 IsConvertibleToInt_Shift = 64-16,
178 IsManaged_Shift = 64-17
182 enum ValueTypeInternal {
183 _Null_Type = Null_Type,
184 _Boolean_Type = Boolean_Type,
185 _Integer_Type = Integer_Type
189 inline unsigned type() const {
190 return tag() & Type_Mask;
193 // used internally in property
194 inline bool isEmpty() const { return tag() == Empty_Type; }
196 inline bool isUndefined() const { return tag() == Undefined_Type; }
197 inline bool isNull() const { return tag() == _Null_Type; }
198 inline bool isBoolean() const { return tag ()== _Boolean_Type; }
199 #if QT_POINTER_SIZE == 8
200 inline bool isInteger() const { return (_val >> IsNumber_Shift) == 1; }
201 inline bool isDouble() const { return (_val >> IsDouble_Shift); }
202 inline bool isNumber() const { return (_val >> IsNumber_Shift); }
203 inline bool isManaged() const { return !(_val >> IsManaged_Shift); }
204 inline bool isNullOrUndefined() const { return ((_val >> IsManaged_Shift) & ~2) == 1; }
205 inline bool integerCompatible() const { return ((_val >> IsConvertibleToInt_Shift) & ~2) == 1; }
206 static inline bool integerCompatible(Value a, Value b) {
207 return a.integerCompatible() && b.integerCompatible();
209 static inline bool bothDouble(Value a, Value b) {
210 return a.isDouble() && b.isDouble();
212 inline bool isNaN() const { return (tag() & 0x7fff8000) == 0x00078000; }
214 inline bool isInteger() const { return tag() == _Integer_Type; }
215 inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; }
216 inline bool isNumber() const { return tag() == _Integer_Type || (tag() & NotDouble_Mask) != NotDouble_Mask; }
217 inline bool isManaged() const { return tag() == Managed_Type; }
218 inline bool isNullOrUndefined() const { return (tag() & IsNullOrUndefined_Mask) == Undefined_Type; }
219 inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; }
220 static inline bool integerCompatible(Value a, Value b) {
221 return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt;
223 static inline bool bothDouble(Value a, Value b) {
224 return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask;
226 inline bool isNaN() const { return (tag() & QV4::Value::NotDouble_Mask) == QV4::Value::NaN_Mask; }
228 Q_ALWAYS_INLINE double doubleValue() const {
229 Q_ASSERT(isDouble());
232 #if QT_POINTER_SIZE == 8
238 Q_ALWAYS_INLINE void setDouble(double d) {
239 memcpy(&_val, &d, 8);
240 #if QT_POINTER_SIZE == 8
241 _val ^= NaNEncodeMask;
243 Q_ASSERT(isDouble());
245 inline bool isString() const;
246 inline bool isObject() const;
247 inline bool isInt32() {
248 if (tag() == _Integer_Type)
251 double d = doubleValue();
255 setTag(_Integer_Type);
261 double asDouble() const {
262 if (tag() == _Integer_Type)
264 return doubleValue();
267 bool booleanValue() const {
270 int integerValue() const {
274 Q_ALWAYS_INLINE String *stringValue() const {
275 return m() ? reinterpret_cast<String*>(const_cast<Value *>(this)) : 0;
277 Q_ALWAYS_INLINE Object *objectValue() const {
278 return m() ? reinterpret_cast<Object*>(const_cast<Value *>(this)) : 0;
280 Q_ALWAYS_INLINE Managed *managed() const {
281 return m() ? reinterpret_cast<Managed*>(const_cast<Value *>(this)) : 0;
283 Q_ALWAYS_INLINE Heap::Base *heapObject() const {
287 Q_ALWAYS_INLINE quint64 &rawValueRef() {
290 Q_ALWAYS_INLINE quint64 rawValue() const {
293 Q_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; }
295 static inline Value fromHeapObject(Heap::Base *m)
300 #if QT_POINTER_SIZE == 4
301 v.setTag(Managed_Type);
306 int toUInt16() const;
307 inline int toInt32() const;
308 inline unsigned int toUInt32() const;
310 bool toBoolean() const;
311 double toInteger() const;
312 inline double toNumber() const;
313 double toNumberImpl() const;
314 QString toQStringNoThrow() const;
315 QString toQString() const;
316 Heap::String *toString(ExecutionEngine *e) const;
317 Heap::Object *toObject(ExecutionEngine *e) const;
319 inline bool isPrimitive() const;
320 inline bool tryIntegerConversion() {
321 bool b = integerCompatible();
323 setTag(_Integer_Type);
327 template <typename T>
328 const T *as() const {
329 if (!m() || !isManaged())
332 Q_ASSERT(m()->vtable());
333 #if !defined(QT_NO_QOBJECT_CHECK)
334 static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this));
336 const VTable *vt = m()->vtable();
338 if (vt == T::staticVTable())
339 return static_cast<const T *>(this);
344 template <typename T>
346 return const_cast<T *>(const_cast<const Value *>(this)->as<T>());
349 template<typename T> inline T *cast() {
350 return static_cast<T *>(managed());
352 template<typename T> inline const T *cast() const {
353 return static_cast<const T *>(managed());
356 inline uint asArrayIndex() const;
358 uint asArrayLength(bool *ok) const;
361 ReturnedValue asReturnedValue() const { return _val; }
362 static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; }
365 bool sameValue(Value other) const;
367 inline void mark(ExecutionEngine *e);
369 Value &operator =(const ScopedValue &v);
370 Value &operator=(ReturnedValue v) { _val = v; return *this; }
371 Value &operator=(Managed *m) {
373 setTagValue(Undefined_Type, 0);
375 _val = reinterpret_cast<Value *>(m)->_val;
379 Value &operator=(Heap::Base *o) {
381 #if QT_POINTER_SIZE == 4
382 setTag(Managed_Type);
388 Value &operator=(const Scoped<T> &t);
389 Value &operator=(const Value &v) {
395 inline bool Value::isString() const
399 return m() && m()->vtable()->isString;
401 inline bool Value::isObject() const
405 return m() && m()->vtable()->isObject;
408 inline bool Value::isPrimitive() const
413 inline double Value::toNumber() const
418 return doubleValue();
419 return toNumberImpl();
424 inline uint Value::asArrayIndex() const
426 #if QT_POINTER_SIZE == 8
430 return int_32() >= 0 ? (uint)int_32() : UINT_MAX;
432 if (isInteger() && int_32() >= 0)
433 return (uint)int_32();
437 double d = doubleValue();
446 ReturnedValue Heap::Base::asReturnedValue() const
448 return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue();
453 struct Q_QML_PRIVATE_EXPORT Primitive : public Value
455 inline static Primitive emptyValue();
456 static inline Primitive fromBoolean(bool b);
457 static inline Primitive fromInt32(int i);
458 inline static Primitive undefinedValue();
459 static inline Primitive nullValue();
460 static inline Primitive fromDouble(double d);
461 static inline Primitive fromUInt32(uint i);
463 using Value::toInt32;
464 using Value::toUInt32;
466 static double toInteger(double fromNumber);
467 static int toInt32(double value);
468 static unsigned int toUInt32(double value);
471 inline Primitive Primitive::undefinedValue()
474 #if QT_POINTER_SIZE == 8
475 v.setRawValue(quint64(Undefined_Type) << Tag_Shift);
478 v.setTag(Undefined_Type);
484 inline Primitive Primitive::emptyValue()
487 v.setTagValue(Value::Empty_Type, 0);
491 inline Primitive Primitive::nullValue()
494 #if QT_POINTER_SIZE == 8
495 v.setRawValue(quint64(_Null_Type) << Tag_Shift);
497 v.setTagValue(_Null_Type, 0);
502 inline Primitive Primitive::fromBoolean(bool b)
505 v.setTagValue(_Boolean_Type, b);
509 inline Primitive Primitive::fromDouble(double d)
516 inline Primitive Primitive::fromInt32(int i)
519 v.setTagValue(_Integer_Type, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors.
524 inline Primitive Primitive::fromUInt32(uint i)
528 v.setTagValue(_Integer_Type, 0); // For mingw482, because it complains, and for VS9, because of internal compiler errors.
537 static ReturnedValue undefined() {
538 return quint64(Value::Undefined_Type) << Value::Tag_Shift;
540 static ReturnedValue null() {
541 return quint64(Value::_Null_Type) << Value::Tag_Shift;
545 val = (quint64(Value::_Boolean_Type) << Value::Tag_Shift) | (uint)b;
553 val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | (uint)i;
557 val = (quint64(Value::_Integer_Type) << Value::Tag_Shift) | i;
564 Encode(ReturnedValue v) {
568 Encode(Heap::Base *o) {
570 val = Value::fromHeapObject(o).asReturnedValue();
573 operator ReturnedValue() const {
582 ReturnedValue value_convert(ExecutionEngine *e, const Value &v);
584 inline int Value::toInt32() const
588 double d = isNumber() ? doubleValue() : toNumberImpl();
590 const double D32 = 4294967296.0;
591 const double D31 = D32 / 2.0;
593 if ((d >= -D31 && d < D31))
594 return static_cast<int>(d);
596 return Primitive::toInt32(d);
599 inline unsigned int Value::toUInt32() const
601 return (unsigned int)toInt32();
609 #endif // QV4VALUE_DEF_P_H