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/. */
8 * Conversions from jsval to primitive values
11 #ifndef mozilla_dom_PrimitiveConversions_h
12 #define mozilla_dom_PrimitiveConversions_h
19 #include "mozilla/Assertions.h"
20 #include "mozilla/ErrorResult.h"
21 #include "mozilla/FloatingPoint.h"
31 struct TypeName
<int8_t> {
32 static const char* value() {
37 struct TypeName
<uint8_t> {
38 static const char* value() {
43 struct TypeName
<int16_t> {
44 static const char* value() {
49 struct TypeName
<uint16_t> {
50 static const char* value() {
51 return "unsigned short";
55 struct TypeName
<int32_t> {
56 static const char* value() {
61 struct TypeName
<uint32_t> {
62 static const char* value() {
63 return "unsigned long";
67 struct TypeName
<int64_t> {
68 static const char* value() {
73 struct TypeName
<uint64_t> {
74 static const char* value() {
75 return "unsigned long long";
80 enum ConversionBehavior
{
86 template<typename T
, ConversionBehavior B
>
87 struct PrimitiveConversionTraits
{
91 struct DisallowedConversion
{
93 typedef int intermediateType
;
96 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
98 MOZ_CRASH("This should never be instantiated!");
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.
111 // The result of all this is a number in the range [-2^31, 2^31)
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.
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
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.
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
,
135 return JS::ToInt32(cx
, v
, retval
);
139 struct PrimitiveConversionTraits
<int8_t, eDefault
> : PrimitiveConversionTraits_smallInt
{
140 typedef uint8_t intermediateType
;
143 struct PrimitiveConversionTraits
<uint8_t, eDefault
> : PrimitiveConversionTraits_smallInt
{
146 struct PrimitiveConversionTraits
<int16_t, eDefault
> : PrimitiveConversionTraits_smallInt
{
147 typedef uint16_t intermediateType
;
150 struct PrimitiveConversionTraits
<uint16_t, eDefault
> : PrimitiveConversionTraits_smallInt
{
153 struct PrimitiveConversionTraits
<int32_t, eDefault
> : PrimitiveConversionTraits_smallInt
{
156 struct PrimitiveConversionTraits
<uint32_t, eDefault
> : PrimitiveConversionTraits_smallInt
{
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
,
165 return JS::ToInt64(cx
, v
, retval
);
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
,
175 return JS::ToUint64(cx
, v
, retval
);
180 struct PrimitiveConversionTraits_Limits
{
181 static inline T
min() {
182 return std::numeric_limits
<T
>::min();
184 static inline T
max() {
185 return std::numeric_limits
<T
>::max();
190 struct PrimitiveConversionTraits_Limits
<int64_t> {
191 static inline int64_t min() {
194 static inline int64_t max() {
200 struct PrimitiveConversionTraits_Limits
<uint64_t> {
201 static inline uint64_t min() {
204 static inline uint64_t max() {
209 template<typename T
, bool (*Enforce
)(JSContext
* cx
, const double& d
, T
* retval
)>
210 struct PrimitiveConversionTraits_ToCheckedIntHelper
{
212 typedef T intermediateType
;
214 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
217 if (!JS::ToNumber(cx
, v
, &intermediate
)) {
221 return Enforce(cx
, intermediate
, retval
);
227 PrimitiveConversionTraits_EnforceRange(JSContext
* cx
, const double& d
, T
* retval
)
229 static_assert(std::numeric_limits
<T
>::is_integer
,
230 "This can only be applied to integers!");
232 if (!mozilla::IsFinite(d
)) {
233 return ThrowErrorMessage(cx
, MSG_ENFORCE_RANGE_NON_FINITE
, TypeName
<T
>::value());
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());
244 *retval
= static_cast<T
>(rounded
);
249 struct PrimitiveConversionTraits
<T
, eEnforceRange
> :
250 public PrimitiveConversionTraits_ToCheckedIntHelper
<T
, PrimitiveConversionTraits_EnforceRange
<T
> > {
255 PrimitiveConversionTraits_Clamp(JSContext
* cx
, const double& d
, T
* retval
)
257 static_assert(std::numeric_limits
<T
>::is_integer
,
258 "This can only be applied to integers!");
260 if (mozilla::IsNaN(d
)) {
264 if (d
>= PrimitiveConversionTraits_Limits
<T
>::max()) {
265 *retval
= PrimitiveConversionTraits_Limits
<T
>::max();
268 if (d
<= PrimitiveConversionTraits_Limits
<T
>::min()) {
269 *retval
= PrimitiveConversionTraits_Limits
<T
>::min();
273 MOZ_ASSERT(mozilla::IsFinite(d
));
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;
282 T truncated
= static_cast<T
>(toTruncate
);
284 if (truncated
== toTruncate
) {
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.
300 struct PrimitiveConversionTraits
<T
, eClamp
> :
301 public PrimitiveConversionTraits_ToCheckedIntHelper
<T
, PrimitiveConversionTraits_Clamp
<T
> > {
305 template<ConversionBehavior B
>
306 struct PrimitiveConversionTraits
<bool, B
> : public DisallowedConversion
<bool> {};
309 struct PrimitiveConversionTraits
<bool, eDefault
> {
311 typedef bool intermediateType
;
312 static inline bool converter(JSContext
* /* unused */, JS::Handle
<JS::Value
> v
,
314 *retval
= JS::ToBoolean(v
);
320 template<ConversionBehavior B
>
321 struct PrimitiveConversionTraits
<float, B
> : public DisallowedConversion
<float> {};
323 template<ConversionBehavior B
>
324 struct PrimitiveConversionTraits
<double, B
> : public DisallowedConversion
<double> {};
326 struct PrimitiveConversionTraits_float
{
327 typedef double jstype
;
328 typedef double intermediateType
;
329 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
331 return JS::ToNumber(cx
, v
, retval
);
336 struct PrimitiveConversionTraits
<float, eDefault
> : PrimitiveConversionTraits_float
{
339 struct PrimitiveConversionTraits
<double, eDefault
> : PrimitiveConversionTraits_float
{
343 template<typename T
, ConversionBehavior B
>
344 bool ValueToPrimitive(JSContext
* cx
, JS::Handle
<JS::Value
> v
, T
* retval
)
346 typename PrimitiveConversionTraits
<T
, B
>::jstype t
;
347 if (!PrimitiveConversionTraits
<T
, B
>::converter(cx
, v
, &t
))
350 *retval
= static_cast<T
>(
351 static_cast<typename PrimitiveConversionTraits
<T
, B
>::intermediateType
>(t
));
356 } // namespace mozilla
358 #endif /* mozilla_dom_PrimitiveConversions_h */