Reviewed by Maciej.
[WebKit-https.git] / JavaScriptCore / kjs / JSImmediate.h
1 /*
2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
3  *  Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Library General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Library General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Library General Public License
16  *  along with this library; see the file COPYING.LIB.  If not, write to
17  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  *  Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #ifndef KJS_JS_IMMEDIATE_H
23 #define KJS_JS_IMMEDIATE_H
24
25 #include "JSType.h"
26 #include <wtf/Assertions.h>
27 #include <wtf/AlwaysInline.h>
28 #include <stdarg.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31
32 namespace KJS {
33
34 class ExecState;
35 class JSObject;
36 class JSValue;
37 class UString;
38
39 /*
40  * A JSValue*  is either a pointer to a cell (a heap-allocated object) or an immediate (a type-tagged 
41  * IEEE floating point bit pattern masquerading as a pointer). The low two bits in a JSValue* are available 
42  * for type tagging because allocator alignment guarantees they will be 00 in cell pointers.
43  *
44  * For example, on a 32 bit system:
45  *
46  * JSCell*:       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                 00
47  *               [ high 30 bits: pointer address ]  [ low 2 bits -- always 0 ]
48  *
49  * JSImmediate:   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX                 TT
50  *             [ high 30 bits: IEEE encoded float ] [ low 2 bits -- type tag ]
51  *
52  * The bit "payload" (the hight 30 bits) of a non-numeric immediate is its numeric equivalent. For example, 
53  * the payload of null is 0.0. This makes JSValue::toNumber() a simple bitmask for all immediates.
54  *
55  * Notice that the JSType value of NullType is 4, which requires 3 bits to encode. Since we only have 2 bits 
56  * available for type tagging, we tag the null immediate with UndefinedType, and JSImmediate::type() has 
57  * to sort them out. Null and Undefined don't otherwise get confused because the numeric value of Undefined is 
58  * NaN, not 0.0.
59  */
60
61 class JSImmediate {
62 public:
63     static ALWAYS_INLINE bool isImmediate(const JSValue* v)
64     {
65         return getTag(v) != 0;
66     }
67     
68     static ALWAYS_INLINE bool isNumber(const JSValue* v)
69     {
70         return (getTag(v) == NumberType);
71     }
72     
73     static ALWAYS_INLINE bool isBoolean(const JSValue* v)
74     {
75         return (getTag(v) == BooleanType);
76     }
77     
78     // Since we have room for only 3 unique tags, null and undefined have to share.
79     static ALWAYS_INLINE bool isUndefinedOrNull(const JSValue* v)
80     {
81         return (getTag(v) == UndefinedType);
82     }
83
84     static JSValue* fromDouble(double d);
85     static double toDouble(const JSValue*);
86     static bool toBoolean(const JSValue*);
87     static JSObject* toObject(const JSValue*, ExecState*);
88     static UString toString(const JSValue*);
89     static JSType type(const JSValue*);
90
91     static bool getUInt32(const JSValue*, uint32_t&);
92     static bool getTruncatedInt32(const JSValue*, int32_t&);
93     static bool getTruncatedUInt32(const JSValue*, uint32_t&);
94
95     // It would nice just to use fromDouble() to create these values, but that would prevent them from
96     // turning into compile-time constants.
97     static JSValue* trueImmediate();
98     static JSValue* falseImmediate();
99     static JSValue* NaNImmediate();
100     static JSValue* undefinedImmediate();
101     static JSValue* nullImmediate();
102     
103 private:
104     static const uintptr_t TagMask = 3; // type tags are 2 bits long
105     
106     static ALWAYS_INLINE JSValue* tag(uintptr_t bits, uintptr_t tag)
107     {
108         return reinterpret_cast<JSValue*>(bits | tag);
109     }
110     
111     static ALWAYS_INLINE uintptr_t unTag(const JSValue* v)
112     {
113         return reinterpret_cast<uintptr_t>(v) & ~TagMask;
114     }
115     
116     static ALWAYS_INLINE uintptr_t getTag(const JSValue* v)
117     {
118         return reinterpret_cast<uintptr_t>(v) & TagMask;
119     }
120     
121     // NOTE: With f-strict-aliasing enabled, unions are the only safe way to do type masquerading.
122
123     union FloatUnion {
124         uint32_t asBits;
125         float    asFloat;
126     };
127
128     union DoubleUnion {
129         uint64_t asBits;
130         double   asDouble;
131     };
132
133     // we support 32-bit platforms with sizes like this
134     static const bool is32bit = 
135         sizeof(float) == sizeof(uint32_t) && sizeof(double) == sizeof(uint64_t) && sizeof(uintptr_t) == sizeof(uint32_t);
136
137     // we support 64-bit platforms with sizes like this
138     static const bool is64bit =
139         sizeof(float) == sizeof(uint32_t) && sizeof(double) == sizeof(uint64_t) && sizeof(uintptr_t) == sizeof(uint64_t);
140
141     template<bool for32bit, bool for64bit> struct FPBitValues {};
142 };
143
144 template<> struct JSImmediate::FPBitValues<true, false> {
145     static const uint32_t nanAsBits = 0x7fc00000;
146     static const uint32_t oneAsBits = 0x3f800000;
147     static const uint32_t zeroAsBits = 0x0;
148
149     static ALWAYS_INLINE JSValue* fromDouble(double d)
150     {
151         FloatUnion floatUnion;
152         floatUnion.asFloat = static_cast<float>(d);
153
154         // check for data loss from tagging
155         if ((floatUnion.asBits & TagMask) != 0)
156             return 0;
157
158         // check for data loss from conversion to float
159         // The d == d check is to allow NaN - it does not
160         // compare equal to itself, but we do want to allow it
161         if (floatUnion.asFloat != d && d == d)
162             return 0;
163
164         return tag(floatUnion.asBits, NumberType);
165     }
166
167     static ALWAYS_INLINE float toFloat(const JSValue* v)
168     {
169         ASSERT(isImmediate(v));
170
171         FloatUnion floatUnion;
172         floatUnion.asBits = static_cast<uint32_t>(unTag(v));
173         return floatUnion.asFloat;
174     }
175
176     static ALWAYS_INLINE double toDouble(const JSValue* v)
177     {
178         return toFloat(v);
179     }
180
181     static ALWAYS_INLINE bool getTruncatedInt32(const JSValue* v, int32_t& i)
182     {
183         float f = toFloat(v);
184         if (!(f >= -2147483648.0F && f < 2147483648.0F))
185             return false;
186         i = static_cast<int32_t>(f);
187         return isNumber(v);
188     }
189
190     static ALWAYS_INLINE bool getTruncatedUInt32(const JSValue* v, uint32_t& i)
191     {
192         float f = toFloat(v);
193         if (!(f >= 0.0F && f < 4294967296.0F))
194             return false;
195         i = static_cast<uint32_t>(f);
196         return isNumber(v);
197     }
198 };
199
200 template<> struct JSImmediate::FPBitValues<false, true> {
201     static const uint64_t nanAsBits = 0x7ff80000ULL << 32;
202     static const uint64_t oneAsBits = 0x3ff00000ULL << 32;
203     static const uint64_t zeroAsBits = 0x0;
204
205     static ALWAYS_INLINE JSValue* fromDouble(double d)
206     {
207         DoubleUnion doubleUnion;
208         doubleUnion.asDouble = d;
209
210         // check for data loss from tagging
211         if ((doubleUnion.asBits & TagMask) != 0)
212             return 0;
213
214         return tag(static_cast<uintptr_t>(doubleUnion.asBits), NumberType);
215     }
216
217     static ALWAYS_INLINE double toDouble(const JSValue* v)
218     {
219         ASSERT(isImmediate(v));
220
221         DoubleUnion doubleUnion;
222         doubleUnion.asBits = unTag(v);
223         return doubleUnion.asDouble;
224     }
225
226     static ALWAYS_INLINE bool getTruncatedInt32(const JSValue* v, int32_t& i)
227     {
228         double d = toDouble(v);
229         if (!(d >= -2147483648.0 && d < 2147483648.0))
230             return false;
231         i = static_cast<int32_t>(d);
232         return isNumber(v);
233     }
234
235     static ALWAYS_INLINE bool getTruncatedUInt32(const JSValue* v, uint32_t& i)
236     {
237         double d = toDouble(v);
238         if (!(d >= 0.0 && d < 4294967296.0))
239             return false;
240         i = static_cast<uint32_t>(d);
241         return isNumber(v);
242     }
243 };
244
245 ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(FPBitValues<is32bit, is64bit>::oneAsBits, BooleanType); }
246 ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(FPBitValues<is32bit, is64bit>::zeroAsBits, BooleanType); }
247 ALWAYS_INLINE JSValue* JSImmediate::NaNImmediate() { return tag(FPBitValues<is32bit, is64bit>::nanAsBits, NumberType); }
248 ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(FPBitValues<is32bit, is64bit>::nanAsBits, UndefinedType); }
249 ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(FPBitValues<is32bit, is64bit>::zeroAsBits, UndefinedType); }
250
251 ALWAYS_INLINE bool JSImmediate::toBoolean(const JSValue* v)
252 {
253     ASSERT(isImmediate(v));
254
255     uintptr_t bits = unTag(v);
256     if ((bits << 1) == 0) // -0.0 has the sign bit set
257         return false;
258
259     return bits != FPBitValues<is32bit, is64bit>::nanAsBits;
260 }
261
262 ALWAYS_INLINE JSValue* JSImmediate::fromDouble(double d)
263 {
264     return FPBitValues<is32bit, is64bit>::fromDouble(d);
265 }
266
267 ALWAYS_INLINE double JSImmediate::toDouble(const JSValue* v)
268 {
269     return FPBitValues<is32bit, is64bit>::toDouble(v);
270 }
271
272 ALWAYS_INLINE bool JSImmediate::getUInt32(const JSValue* v, uint32_t& i)
273 {
274     double d = toDouble(v);
275     i = static_cast<uint32_t>(d);
276     return isNumber(v) & (i == d);
277 }
278
279 ALWAYS_INLINE bool JSImmediate::getTruncatedInt32(const JSValue* v, int32_t& i)
280 {
281     return FPBitValues<is32bit, is64bit>::getTruncatedInt32(v, i);
282 }
283
284 ALWAYS_INLINE bool JSImmediate::getTruncatedUInt32(const JSValue* v, uint32_t& i)
285 {
286     return FPBitValues<is32bit, is64bit>::getTruncatedUInt32(v, i);
287 }
288
289 } // namespace KJS
290
291 #endif