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