|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
|
2 /* vim: set ts=2 sw=2 et tw=79: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /** |
|
8 * Conversions from jsval to primitive values |
|
9 */ |
|
10 |
|
11 #ifndef mozilla_dom_PrimitiveConversions_h |
|
12 #define mozilla_dom_PrimitiveConversions_h |
|
13 |
|
14 #include <limits> |
|
15 #include <math.h> |
|
16 #include <stdint.h> |
|
17 |
|
18 #include "jsapi.h" |
|
19 #include "mozilla/Assertions.h" |
|
20 #include "mozilla/ErrorResult.h" |
|
21 #include "mozilla/FloatingPoint.h" |
|
22 |
|
23 namespace mozilla { |
|
24 namespace dom { |
|
25 |
|
26 template<typename T> |
|
27 struct TypeName { |
|
28 }; |
|
29 |
|
30 template<> |
|
31 struct TypeName<int8_t> { |
|
32 static const char* value() { |
|
33 return "byte"; |
|
34 } |
|
35 }; |
|
36 template<> |
|
37 struct TypeName<uint8_t> { |
|
38 static const char* value() { |
|
39 return "octet"; |
|
40 } |
|
41 }; |
|
42 template<> |
|
43 struct TypeName<int16_t> { |
|
44 static const char* value() { |
|
45 return "short"; |
|
46 } |
|
47 }; |
|
48 template<> |
|
49 struct TypeName<uint16_t> { |
|
50 static const char* value() { |
|
51 return "unsigned short"; |
|
52 } |
|
53 }; |
|
54 template<> |
|
55 struct TypeName<int32_t> { |
|
56 static const char* value() { |
|
57 return "long"; |
|
58 } |
|
59 }; |
|
60 template<> |
|
61 struct TypeName<uint32_t> { |
|
62 static const char* value() { |
|
63 return "unsigned long"; |
|
64 } |
|
65 }; |
|
66 template<> |
|
67 struct TypeName<int64_t> { |
|
68 static const char* value() { |
|
69 return "long long"; |
|
70 } |
|
71 }; |
|
72 template<> |
|
73 struct TypeName<uint64_t> { |
|
74 static const char* value() { |
|
75 return "unsigned long long"; |
|
76 } |
|
77 }; |
|
78 |
|
79 |
|
80 enum ConversionBehavior { |
|
81 eDefault, |
|
82 eEnforceRange, |
|
83 eClamp |
|
84 }; |
|
85 |
|
86 template<typename T, ConversionBehavior B> |
|
87 struct PrimitiveConversionTraits { |
|
88 }; |
|
89 |
|
90 template<typename T> |
|
91 struct DisallowedConversion { |
|
92 typedef int jstype; |
|
93 typedef int intermediateType; |
|
94 |
|
95 private: |
|
96 static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, |
|
97 jstype* retval) { |
|
98 MOZ_CRASH("This should never be instantiated!"); |
|
99 } |
|
100 }; |
|
101 |
|
102 struct PrimitiveConversionTraits_smallInt { |
|
103 // The output of JS::ToInt32 is determined as follows: |
|
104 // 1) The value is converted to a double |
|
105 // 2) Anything that's not a finite double returns 0 |
|
106 // 3) The double is rounded towards zero to the nearest integer |
|
107 // 4) The resulting integer is reduced mod 2^32. The output of this |
|
108 // operation is an integer in the range [0, 2^32). |
|
109 // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it. |
|
110 // |
|
111 // The result of all this is a number in the range [-2^31, 2^31) |
|
112 // |
|
113 // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types |
|
114 // are defined in the same way, except that step 4 uses reduction mod |
|
115 // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5 |
|
116 // is only done for the signed types. |
|
117 // |
|
118 // C/C++ define integer conversion semantics to unsigned types as taking |
|
119 // your input integer mod (1 + largest value representable in the |
|
120 // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32, |
|
121 // converting to the unsigned int of the relevant width will correctly |
|
122 // perform step 4; in particular, the 2^32 possibly subtracted in step 5 |
|
123 // will become 0. |
|
124 // |
|
125 // Once we have step 4 done, we're just going to assume 2s-complement |
|
126 // representation and cast directly to the type we really want. |
|
127 // |
|
128 // So we can cast directly for all unsigned types and for int32_t; for |
|
129 // the smaller-width signed types we need to cast through the |
|
130 // corresponding unsigned type. |
|
131 typedef int32_t jstype; |
|
132 typedef int32_t intermediateType; |
|
133 static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, |
|
134 jstype* retval) { |
|
135 return JS::ToInt32(cx, v, retval); |
|
136 } |
|
137 }; |
|
138 template<> |
|
139 struct PrimitiveConversionTraits<int8_t, eDefault> : PrimitiveConversionTraits_smallInt { |
|
140 typedef uint8_t intermediateType; |
|
141 }; |
|
142 template<> |
|
143 struct PrimitiveConversionTraits<uint8_t, eDefault> : PrimitiveConversionTraits_smallInt { |
|
144 }; |
|
145 template<> |
|
146 struct PrimitiveConversionTraits<int16_t, eDefault> : PrimitiveConversionTraits_smallInt { |
|
147 typedef uint16_t intermediateType; |
|
148 }; |
|
149 template<> |
|
150 struct PrimitiveConversionTraits<uint16_t, eDefault> : PrimitiveConversionTraits_smallInt { |
|
151 }; |
|
152 template<> |
|
153 struct PrimitiveConversionTraits<int32_t, eDefault> : PrimitiveConversionTraits_smallInt { |
|
154 }; |
|
155 template<> |
|
156 struct PrimitiveConversionTraits<uint32_t, eDefault> : PrimitiveConversionTraits_smallInt { |
|
157 }; |
|
158 |
|
159 template<> |
|
160 struct PrimitiveConversionTraits<int64_t, eDefault> { |
|
161 typedef int64_t jstype; |
|
162 typedef int64_t intermediateType; |
|
163 static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, |
|
164 jstype* retval) { |
|
165 return JS::ToInt64(cx, v, retval); |
|
166 } |
|
167 }; |
|
168 |
|
169 template<> |
|
170 struct PrimitiveConversionTraits<uint64_t, eDefault> { |
|
171 typedef uint64_t jstype; |
|
172 typedef uint64_t intermediateType; |
|
173 static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, |
|
174 jstype* retval) { |
|
175 return JS::ToUint64(cx, v, retval); |
|
176 } |
|
177 }; |
|
178 |
|
179 template<typename T> |
|
180 struct PrimitiveConversionTraits_Limits { |
|
181 static inline T min() { |
|
182 return std::numeric_limits<T>::min(); |
|
183 } |
|
184 static inline T max() { |
|
185 return std::numeric_limits<T>::max(); |
|
186 } |
|
187 }; |
|
188 |
|
189 template<> |
|
190 struct PrimitiveConversionTraits_Limits<int64_t> { |
|
191 static inline int64_t min() { |
|
192 return -(1LL << 53); |
|
193 } |
|
194 static inline int64_t max() { |
|
195 return (1LL << 53); |
|
196 } |
|
197 }; |
|
198 |
|
199 template<> |
|
200 struct PrimitiveConversionTraits_Limits<uint64_t> { |
|
201 static inline uint64_t min() { |
|
202 return 0; |
|
203 } |
|
204 static inline uint64_t max() { |
|
205 return (1LL << 53); |
|
206 } |
|
207 }; |
|
208 |
|
209 template<typename T, bool (*Enforce)(JSContext* cx, const double& d, T* retval)> |
|
210 struct PrimitiveConversionTraits_ToCheckedIntHelper { |
|
211 typedef T jstype; |
|
212 typedef T intermediateType; |
|
213 |
|
214 static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, |
|
215 jstype* retval) { |
|
216 double intermediate; |
|
217 if (!JS::ToNumber(cx, v, &intermediate)) { |
|
218 return false; |
|
219 } |
|
220 |
|
221 return Enforce(cx, intermediate, retval); |
|
222 } |
|
223 }; |
|
224 |
|
225 template<typename T> |
|
226 inline bool |
|
227 PrimitiveConversionTraits_EnforceRange(JSContext* cx, const double& d, T* retval) |
|
228 { |
|
229 static_assert(std::numeric_limits<T>::is_integer, |
|
230 "This can only be applied to integers!"); |
|
231 |
|
232 if (!mozilla::IsFinite(d)) { |
|
233 return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_NON_FINITE, TypeName<T>::value()); |
|
234 } |
|
235 |
|
236 bool neg = (d < 0); |
|
237 double rounded = floor(neg ? -d : d); |
|
238 rounded = neg ? -rounded : rounded; |
|
239 if (rounded < PrimitiveConversionTraits_Limits<T>::min() || |
|
240 rounded > PrimitiveConversionTraits_Limits<T>::max()) { |
|
241 return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_OUT_OF_RANGE, TypeName<T>::value()); |
|
242 } |
|
243 |
|
244 *retval = static_cast<T>(rounded); |
|
245 return true; |
|
246 } |
|
247 |
|
248 template<typename T> |
|
249 struct PrimitiveConversionTraits<T, eEnforceRange> : |
|
250 public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_EnforceRange<T> > { |
|
251 }; |
|
252 |
|
253 template<typename T> |
|
254 inline bool |
|
255 PrimitiveConversionTraits_Clamp(JSContext* cx, const double& d, T* retval) |
|
256 { |
|
257 static_assert(std::numeric_limits<T>::is_integer, |
|
258 "This can only be applied to integers!"); |
|
259 |
|
260 if (mozilla::IsNaN(d)) { |
|
261 *retval = 0; |
|
262 return true; |
|
263 } |
|
264 if (d >= PrimitiveConversionTraits_Limits<T>::max()) { |
|
265 *retval = PrimitiveConversionTraits_Limits<T>::max(); |
|
266 return true; |
|
267 } |
|
268 if (d <= PrimitiveConversionTraits_Limits<T>::min()) { |
|
269 *retval = PrimitiveConversionTraits_Limits<T>::min(); |
|
270 return true; |
|
271 } |
|
272 |
|
273 MOZ_ASSERT(mozilla::IsFinite(d)); |
|
274 |
|
275 // Banker's rounding (round ties towards even). |
|
276 // We move away from 0 by 0.5f and then truncate. That gets us the right |
|
277 // answer for any starting value except plus or minus N.5. With a starting |
|
278 // value of that form, we now have plus or minus N+1. If N is odd, this is |
|
279 // the correct result. If N is even, plus or minus N is the correct result. |
|
280 double toTruncate = (d < 0) ? d - 0.5 : d + 0.5; |
|
281 |
|
282 T truncated = static_cast<T>(toTruncate); |
|
283 |
|
284 if (truncated == toTruncate) { |
|
285 /* |
|
286 * It was a tie (since moving away from 0 by 0.5 gave us the exact integer |
|
287 * we want). Since we rounded away from 0, we either already have an even |
|
288 * number or we have an odd number but the number we want is one closer to |
|
289 * 0. So just unconditionally masking out the ones bit should do the trick |
|
290 * to get us the value we want. |
|
291 */ |
|
292 truncated &= ~1; |
|
293 } |
|
294 |
|
295 *retval = truncated; |
|
296 return true; |
|
297 } |
|
298 |
|
299 template<typename T> |
|
300 struct PrimitiveConversionTraits<T, eClamp> : |
|
301 public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_Clamp<T> > { |
|
302 }; |
|
303 |
|
304 |
|
305 template<ConversionBehavior B> |
|
306 struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> {}; |
|
307 |
|
308 template<> |
|
309 struct PrimitiveConversionTraits<bool, eDefault> { |
|
310 typedef bool jstype; |
|
311 typedef bool intermediateType; |
|
312 static inline bool converter(JSContext* /* unused */, JS::Handle<JS::Value> v, |
|
313 jstype* retval) { |
|
314 *retval = JS::ToBoolean(v); |
|
315 return true; |
|
316 } |
|
317 }; |
|
318 |
|
319 |
|
320 template<ConversionBehavior B> |
|
321 struct PrimitiveConversionTraits<float, B> : public DisallowedConversion<float> {}; |
|
322 |
|
323 template<ConversionBehavior B> |
|
324 struct PrimitiveConversionTraits<double, B> : public DisallowedConversion<double> {}; |
|
325 |
|
326 struct PrimitiveConversionTraits_float { |
|
327 typedef double jstype; |
|
328 typedef double intermediateType; |
|
329 static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, |
|
330 jstype* retval) { |
|
331 return JS::ToNumber(cx, v, retval); |
|
332 } |
|
333 }; |
|
334 |
|
335 template<> |
|
336 struct PrimitiveConversionTraits<float, eDefault> : PrimitiveConversionTraits_float { |
|
337 }; |
|
338 template<> |
|
339 struct PrimitiveConversionTraits<double, eDefault> : PrimitiveConversionTraits_float { |
|
340 }; |
|
341 |
|
342 |
|
343 template<typename T, ConversionBehavior B> |
|
344 bool ValueToPrimitive(JSContext* cx, JS::Handle<JS::Value> v, T* retval) |
|
345 { |
|
346 typename PrimitiveConversionTraits<T, B>::jstype t; |
|
347 if (!PrimitiveConversionTraits<T, B>::converter(cx, v, &t)) |
|
348 return false; |
|
349 |
|
350 *retval = static_cast<T>( |
|
351 static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(t)); |
|
352 return true; |
|
353 } |
|
354 |
|
355 } // namespace dom |
|
356 } // namespace mozilla |
|
357 |
|
358 #endif /* mozilla_dom_PrimitiveConversions_h */ |