1 | /* |
2 | * This file is part of the KDE libraries |
3 | * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
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 <kjs/global.h> |
27 | #include "JSType.h" |
28 | #include <wtf/Assertions.h> |
29 | #include <wtf/AlwaysInline.h> |
30 | #include <wtf/MathExtras.h> |
31 | #ifdef HAVE_STDINT_H |
32 | #include <stdint.h> |
33 | #endif |
34 | #include <stdlib.h> |
35 | |
36 | #if PLATFORM(SOLARIS_OS) |
37 | static inline int signbit(double x) |
38 | { |
39 | return (x<0.0) ? 1 : 0; |
40 | } |
41 | #endif |
42 | |
43 | namespace KJS { |
44 | |
45 | class ExecState; |
46 | class JSObject; |
47 | class JSValue; |
48 | class UString; |
49 | |
50 | KJS_EXPORT extern const double NaN; |
51 | KJS_EXPORT extern const double Inf; |
52 | |
53 | /* |
54 | * A JSValue* is either a pointer to a cell (a heap-allocated object) or an immediate (a type-tagged |
55 | * signed int masquerading as a pointer). The low two bits in a JSValue* are available |
56 | * for type tagging because allocator alignment guarantees they will be 00 in cell pointers. |
57 | * |
58 | * For example, on a 32 bit system: |
59 | * |
60 | * JSCell*: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 00 |
61 | * [ high 30 bits: pointer address ] [ low 2 bits -- always 0 ] |
62 | * |
63 | * JSImmediate: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX TT |
64 | * [ high 30 bits: signed int ] [ low 2 bits -- type tag ] |
65 | * |
66 | * The bit "payload" (the high 30 bits) is a 30 bit signed int for immediate numbers, a flag to distinguish true/false |
67 | * and undefined/null. |
68 | * |
69 | * Notice that the JSType value of NullType is 4, which requires 3 bits to encode. Since we only have 2 bits |
70 | * available for type tagging, we tag the null immediate with UndefinedType, and JSImmediate::type() has |
71 | * to sort them out. |
72 | */ |
73 | |
74 | class KJS_EXPORT JSImmediate { |
75 | public: |
76 | static ALWAYS_INLINE bool isImmediate(const JSValue* v) |
77 | { |
78 | return getTag(v) != 0; |
79 | } |
80 | |
81 | static ALWAYS_INLINE bool isNumber(const JSValue* v) |
82 | { |
83 | return (getTag(v) == NumberType); |
84 | } |
85 | |
86 | static ALWAYS_INLINE bool isBoolean(const JSValue* v) |
87 | { |
88 | return (getTag(v) == BooleanType); |
89 | } |
90 | |
91 | // Since we have room for only 3 unique tags, null and undefined have to share. |
92 | static ALWAYS_INLINE bool isUndefinedOrNull(const JSValue* v) |
93 | { |
94 | return (getTag(v) == UndefinedType); |
95 | } |
96 | |
97 | static JSValue* from(char); |
98 | static JSValue* from(signed char); |
99 | static JSValue* from(unsigned char); |
100 | static JSValue* from(short); |
101 | static JSValue* from(unsigned short); |
102 | static JSValue* from(int); |
103 | static JSValue* from(unsigned); |
104 | static JSValue* from(long); |
105 | static JSValue* from(unsigned long); |
106 | static JSValue* from(long long); |
107 | static JSValue* from(unsigned long long); |
108 | static JSValue* from(double); |
109 | |
110 | static ALWAYS_INLINE bool areBothImmediateNumbers(const JSValue* v1, const JSValue* v2) |
111 | { |
112 | return (reinterpret_cast<uintptr_t>(v1) & reinterpret_cast<uintptr_t>(v2) & TagMask) == NumberType; |
113 | } |
114 | |
115 | static ALWAYS_INLINE JSValue* andImmediateNumbers(const JSValue* v1, const JSValue* v2) |
116 | { |
117 | ASSERT(areBothImmediateNumbers(v1, v2)); |
118 | return reinterpret_cast<JSValue*>(reinterpret_cast<uintptr_t>(v1) & reinterpret_cast<uintptr_t>(v2)); |
119 | } |
120 | |
121 | static double toDouble(const JSValue*); |
122 | |
123 | // Non-converting getters for Number's |
124 | static double getNumber(const JSValue*); // This returns NaN if the value is not a Number. |
125 | static bool getNumber(const JSValue*, double& valOut); |
126 | |
127 | static bool toBoolean(const JSValue*); |
128 | static JSObject* toObject(const JSValue*, ExecState*); |
129 | static UString toString(const JSValue*); |
130 | static JSType type(const JSValue*); |
131 | |
132 | static bool getUInt32(const JSValue*, uint32_t&); |
133 | static bool getTruncatedInt32(const JSValue*, int32_t&); |
134 | static bool getTruncatedUInt32(const JSValue*, uint32_t&); |
135 | |
136 | static int32_t getTruncatedInt32(const JSValue*); |
137 | |
138 | static JSValue* trueImmediate(); |
139 | static JSValue* falseImmediate(); |
140 | static JSValue* undefinedImmediate(); |
141 | static JSValue* nullImmediate(); |
142 | |
143 | private: |
144 | static const uintptr_t TagMask = 3; // type tags are 2 bits long |
145 | |
146 | // Immediate values are restricted to a 30 bit signed value. |
147 | static const int minImmediateInt = -(1 << 29); |
148 | static const int maxImmediateInt = (1 << 29) - 1; |
149 | static const unsigned maxImmediateUInt = maxImmediateInt; |
150 | |
151 | static ALWAYS_INLINE JSValue* tag(uintptr_t bits, uintptr_t tag) |
152 | { |
153 | return reinterpret_cast<JSValue*>(bits | tag); |
154 | } |
155 | |
156 | static ALWAYS_INLINE uintptr_t unTag(const JSValue* v) |
157 | { |
158 | return reinterpret_cast<uintptr_t>(v) & ~TagMask; |
159 | } |
160 | |
161 | static ALWAYS_INLINE uintptr_t getTag(const JSValue* v) |
162 | { |
163 | return reinterpret_cast<uintptr_t>(v) & TagMask; |
164 | } |
165 | }; |
166 | |
167 | ALWAYS_INLINE JSValue* JSImmediate::trueImmediate() { return tag(1 << 2, BooleanType); } |
168 | ALWAYS_INLINE JSValue* JSImmediate::falseImmediate() { return tag(0, BooleanType); } |
169 | ALWAYS_INLINE JSValue* JSImmediate::undefinedImmediate() { return tag(1 << 2, UndefinedType); } |
170 | ALWAYS_INLINE JSValue* JSImmediate::nullImmediate() { return tag(0, UndefinedType); } |
171 | |
172 | ALWAYS_INLINE bool JSImmediate::toBoolean(const JSValue* v) |
173 | { |
174 | ASSERT(isImmediate(v)); |
175 | uintptr_t bits = unTag(v); |
176 | return (bits != 0) & (JSImmediate::getTag(v) != UndefinedType); |
177 | } |
178 | |
179 | ALWAYS_INLINE JSValue* JSImmediate::from(char i) |
180 | { |
181 | return tag(i << 2, NumberType); |
182 | } |
183 | |
184 | ALWAYS_INLINE JSValue* JSImmediate::from(signed char i) |
185 | { |
186 | return tag(i << 2, NumberType); |
187 | } |
188 | |
189 | ALWAYS_INLINE JSValue* JSImmediate::from(unsigned char i) |
190 | { |
191 | return tag(i << 2, NumberType); |
192 | } |
193 | |
194 | ALWAYS_INLINE JSValue* JSImmediate::from(short i) |
195 | { |
196 | return tag(i << 2, NumberType); |
197 | } |
198 | |
199 | ALWAYS_INLINE JSValue* JSImmediate::from(unsigned short i) |
200 | { |
201 | return tag(i << 2, NumberType); |
202 | } |
203 | |
204 | ALWAYS_INLINE JSValue* JSImmediate::from(int i) |
205 | { |
206 | if ((i < minImmediateInt) || (i > maxImmediateInt)) |
207 | return 0; |
208 | return tag(i << 2, NumberType); |
209 | } |
210 | |
211 | ALWAYS_INLINE JSValue* JSImmediate::from(unsigned i) |
212 | { |
213 | if (i > maxImmediateUInt) |
214 | return 0; |
215 | return tag(i << 2, NumberType); |
216 | } |
217 | |
218 | ALWAYS_INLINE JSValue* JSImmediate::from(long i) |
219 | { |
220 | if ((i < minImmediateInt) || (i > maxImmediateInt)) |
221 | return 0; |
222 | return tag(i << 2, NumberType); |
223 | } |
224 | |
225 | ALWAYS_INLINE JSValue* JSImmediate::from(unsigned long i) |
226 | { |
227 | if (i > maxImmediateUInt) |
228 | return 0; |
229 | return tag(i << 2, NumberType); |
230 | } |
231 | |
232 | ALWAYS_INLINE JSValue* JSImmediate::from(long long i) |
233 | { |
234 | if ((i < minImmediateInt) || (i > maxImmediateInt)) |
235 | return 0; |
236 | return tag(static_cast<uintptr_t>(i) << 2, NumberType); |
237 | } |
238 | |
239 | ALWAYS_INLINE JSValue* JSImmediate::from(unsigned long long i) |
240 | { |
241 | if (i > maxImmediateUInt) |
242 | return 0; |
243 | return tag(static_cast<uintptr_t>(i) << 2, NumberType); |
244 | } |
245 | |
246 | ALWAYS_INLINE JSValue* JSImmediate::from(double d) |
247 | { |
248 | const int intVal = static_cast<int>(d); |
249 | |
250 | if ((intVal < minImmediateInt) || (intVal > maxImmediateInt)) |
251 | return 0; |
252 | |
253 | // Check for data loss from conversion to int. |
254 | if ((intVal != d) || (!intVal && signbit(d))) |
255 | return 0; |
256 | |
257 | return tag(intVal << 2, NumberType); |
258 | } |
259 | |
260 | ALWAYS_INLINE int32_t JSImmediate::getTruncatedInt32(const JSValue* v) |
261 | { |
262 | ASSERT(isNumber(v)); |
263 | return static_cast<int32_t>(unTag(v)) >> 2; |
264 | } |
265 | |
266 | ALWAYS_INLINE double JSImmediate::toDouble(const JSValue* v) |
267 | { |
268 | ASSERT(isImmediate(v)); |
269 | const int32_t i = static_cast<int32_t>(unTag(v)) >> 2; |
270 | if (JSImmediate::getTag(v) == UndefinedType && i) |
271 | return NaN; |
272 | return i; |
273 | } |
274 | |
275 | ALWAYS_INLINE double JSImmediate::getNumber(const JSValue* v) |
276 | { |
277 | ASSERT(isImmediate(v)); |
278 | const int32_t i = static_cast<int32_t>(unTag(v)) >> 2; |
279 | if (JSImmediate::getTag(v) != NumberType) |
280 | return NaN; |
281 | return i; |
282 | } |
283 | |
284 | ALWAYS_INLINE bool JSImmediate::getNumber(const JSValue* v, double& numberOut) |
285 | { |
286 | ASSERT(isImmediate(v)); |
287 | numberOut = static_cast<int32_t>(unTag(v)) >> 2; |
288 | return (JSImmediate::getTag(v) == NumberType); |
289 | } |
290 | |
291 | ALWAYS_INLINE bool JSImmediate::getUInt32(const JSValue* v, uint32_t& i) |
292 | { |
293 | const int32_t si = static_cast<int32_t>(unTag(v)) >> 2; |
294 | i = si; |
295 | return isNumber(v) & (si >= 0); |
296 | } |
297 | |
298 | ALWAYS_INLINE bool JSImmediate::getTruncatedInt32(const JSValue* v, int32_t& i) |
299 | { |
300 | i = static_cast<int32_t>(unTag(v)) >> 2; |
301 | return isNumber(v); |
302 | } |
303 | |
304 | ALWAYS_INLINE bool JSImmediate::getTruncatedUInt32(const JSValue* v, uint32_t& i) |
305 | { |
306 | return getUInt32(v, i); |
307 | } |
308 | |
309 | } // namespace KJS |
310 | |
311 | #endif |
312 | |