1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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
18 #include "js/Conversions.h"
19 #include "js/RootingAPI.h"
20 #include "mozilla/Assertions.h"
21 #include "mozilla/FloatingPoint.h"
22 #include "mozilla/dom/BindingCallContext.h"
24 namespace mozilla::dom
{
30 struct TypeName
<int8_t> {
31 static const char* value() { return "byte"; }
34 struct TypeName
<uint8_t> {
35 static const char* value() { return "octet"; }
38 struct TypeName
<int16_t> {
39 static const char* value() { return "short"; }
42 struct TypeName
<uint16_t> {
43 static const char* value() { return "unsigned short"; }
46 struct TypeName
<int32_t> {
47 static const char* value() { return "long"; }
50 struct TypeName
<uint32_t> {
51 static const char* value() { return "unsigned long"; }
54 struct TypeName
<int64_t> {
55 static const char* value() { return "long long"; }
58 struct TypeName
<uint64_t> {
59 static const char* value() { return "unsigned long long"; }
62 enum ConversionBehavior
{ eDefault
, eEnforceRange
, eClamp
};
64 template <typename T
, ConversionBehavior B
>
65 struct PrimitiveConversionTraits
{};
68 struct DisallowedConversion
{
70 typedef int intermediateType
;
73 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
74 const char* sourceDescription
, jstype
* retval
) {
75 MOZ_CRASH("This should never be instantiated!");
79 struct PrimitiveConversionTraits_smallInt
{
80 // The output of JS::ToInt32 is determined as follows:
81 // 1) The value is converted to a double
82 // 2) Anything that's not a finite double returns 0
83 // 3) The double is rounded towards zero to the nearest integer
84 // 4) The resulting integer is reduced mod 2^32. The output of this
85 // operation is an integer in the range [0, 2^32).
86 // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
88 // The result of all this is a number in the range [-2^31, 2^31)
90 // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
91 // are defined in the same way, except that step 4 uses reduction mod
92 // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
93 // is only done for the signed types.
95 // C/C++ define integer conversion semantics to unsigned types as taking
96 // your input integer mod (1 + largest value representable in the
97 // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
98 // converting to the unsigned int of the relevant width will correctly
99 // perform step 4; in particular, the 2^32 possibly subtracted in step 5
102 // Once we have step 4 done, we're just going to assume 2s-complement
103 // representation and cast directly to the type we really want.
105 // So we can cast directly for all unsigned types and for int32_t; for
106 // the smaller-width signed types we need to cast through the
107 // corresponding unsigned type.
108 typedef int32_t jstype
;
109 typedef int32_t intermediateType
;
110 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
111 const char* sourceDescription
, jstype
* retval
) {
112 return JS::ToInt32(cx
, v
, retval
);
116 struct PrimitiveConversionTraits
<int8_t, eDefault
>
117 : PrimitiveConversionTraits_smallInt
{
118 typedef uint8_t intermediateType
;
121 struct PrimitiveConversionTraits
<uint8_t, eDefault
>
122 : PrimitiveConversionTraits_smallInt
{};
124 struct PrimitiveConversionTraits
<int16_t, eDefault
>
125 : PrimitiveConversionTraits_smallInt
{
126 typedef uint16_t intermediateType
;
129 struct PrimitiveConversionTraits
<uint16_t, eDefault
>
130 : PrimitiveConversionTraits_smallInt
{};
132 struct PrimitiveConversionTraits
<int32_t, eDefault
>
133 : PrimitiveConversionTraits_smallInt
{};
135 struct PrimitiveConversionTraits
<uint32_t, eDefault
>
136 : PrimitiveConversionTraits_smallInt
{};
139 struct PrimitiveConversionTraits
<int64_t, eDefault
> {
140 typedef int64_t jstype
;
141 typedef int64_t intermediateType
;
142 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
143 const char* sourceDescription
, jstype
* retval
) {
144 return JS::ToInt64(cx
, v
, retval
);
149 struct PrimitiveConversionTraits
<uint64_t, eDefault
> {
150 typedef uint64_t jstype
;
151 typedef uint64_t intermediateType
;
152 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
153 const char* sourceDescription
, jstype
* retval
) {
154 return JS::ToUint64(cx
, v
, retval
);
158 template <typename T
>
159 struct PrimitiveConversionTraits_Limits
{
160 static inline T
min() { return std::numeric_limits
<T
>::min(); }
161 static inline T
max() { return std::numeric_limits
<T
>::max(); }
165 struct PrimitiveConversionTraits_Limits
<int64_t> {
166 static inline int64_t min() { return -(1LL << 53) + 1; }
167 static inline int64_t max() { return (1LL << 53) - 1; }
171 struct PrimitiveConversionTraits_Limits
<uint64_t> {
172 static inline uint64_t min() { return 0; }
173 static inline uint64_t max() { return (1LL << 53) - 1; }
176 template <typename T
, typename U
,
177 bool (*Enforce
)(U cx
, const char* sourceDescription
, const double& d
,
179 struct PrimitiveConversionTraits_ToCheckedIntHelper
{
181 typedef T intermediateType
;
183 static inline bool converter(U cx
, JS::Handle
<JS::Value
> v
,
184 const char* sourceDescription
, jstype
* retval
) {
186 if (!JS::ToNumber(cx
, v
, &intermediate
)) {
190 return Enforce(cx
, sourceDescription
, intermediate
, retval
);
194 template <typename T
>
195 inline bool PrimitiveConversionTraits_EnforceRange(
196 BindingCallContext
& cx
, const char* sourceDescription
, const double& d
,
198 static_assert(std::numeric_limits
<T
>::is_integer
,
199 "This can only be applied to integers!");
201 if (!std::isfinite(d
)) {
202 return cx
.ThrowErrorMessage
<MSG_ENFORCE_RANGE_NON_FINITE
>(
203 sourceDescription
, TypeName
<T
>::value());
207 double rounded
= floor(neg
? -d
: d
);
208 rounded
= neg
? -rounded
: rounded
;
209 if (rounded
< PrimitiveConversionTraits_Limits
<T
>::min() ||
210 rounded
> PrimitiveConversionTraits_Limits
<T
>::max()) {
211 return cx
.ThrowErrorMessage
<MSG_ENFORCE_RANGE_OUT_OF_RANGE
>(
212 sourceDescription
, TypeName
<T
>::value());
215 *retval
= static_cast<T
>(rounded
);
219 template <typename T
>
220 struct PrimitiveConversionTraits
<T
, eEnforceRange
>
221 : public PrimitiveConversionTraits_ToCheckedIntHelper
<
222 T
, BindingCallContext
&, PrimitiveConversionTraits_EnforceRange
<T
> > {
225 template <typename T
>
226 inline bool PrimitiveConversionTraits_Clamp(JSContext
* cx
,
227 const char* sourceDescription
,
228 const double& d
, T
* retval
) {
229 static_assert(std::numeric_limits
<T
>::is_integer
,
230 "This can only be applied to integers!");
236 if (d
>= PrimitiveConversionTraits_Limits
<T
>::max()) {
237 *retval
= PrimitiveConversionTraits_Limits
<T
>::max();
240 if (d
<= PrimitiveConversionTraits_Limits
<T
>::min()) {
241 *retval
= PrimitiveConversionTraits_Limits
<T
>::min();
245 MOZ_ASSERT(std::isfinite(d
));
247 // Banker's rounding (round ties towards even).
248 // We move away from 0 by 0.5f and then truncate. That gets us the right
249 // answer for any starting value except plus or minus N.5. With a starting
250 // value of that form, we now have plus or minus N+1. If N is odd, this is
251 // the correct result. If N is even, plus or minus N is the correct result.
252 double toTruncate
= (d
< 0) ? d
- 0.5 : d
+ 0.5;
254 T truncated
= static_cast<T
>(toTruncate
);
256 if (truncated
== toTruncate
) {
258 * It was a tie (since moving away from 0 by 0.5 gave us the exact integer
259 * we want). Since we rounded away from 0, we either already have an even
260 * number or we have an odd number but the number we want is one closer to
261 * 0. So just unconditionally masking out the ones bit should do the trick
262 * to get us the value we want.
271 template <typename T
>
272 struct PrimitiveConversionTraits
<T
, eClamp
>
273 : public PrimitiveConversionTraits_ToCheckedIntHelper
<
274 T
, JSContext
*, PrimitiveConversionTraits_Clamp
<T
> > {};
276 template <ConversionBehavior B
>
277 struct PrimitiveConversionTraits
<bool, B
> : public DisallowedConversion
<bool> {
281 struct PrimitiveConversionTraits
<bool, eDefault
> {
283 typedef bool intermediateType
;
284 static inline bool converter(JSContext
* /* unused */, JS::Handle
<JS::Value
> v
,
285 const char* sourceDescription
, jstype
* retval
) {
286 *retval
= JS::ToBoolean(v
);
291 template <ConversionBehavior B
>
292 struct PrimitiveConversionTraits
<float, B
>
293 : public DisallowedConversion
<float> {};
295 template <ConversionBehavior B
>
296 struct PrimitiveConversionTraits
<double, B
>
297 : public DisallowedConversion
<double> {};
299 struct PrimitiveConversionTraits_float
{
300 typedef double jstype
;
301 typedef double intermediateType
;
302 static inline bool converter(JSContext
* cx
, JS::Handle
<JS::Value
> v
,
303 const char* sourceDescription
, jstype
* retval
) {
304 return JS::ToNumber(cx
, v
, retval
);
309 struct PrimitiveConversionTraits
<float, eDefault
>
310 : PrimitiveConversionTraits_float
{};
312 struct PrimitiveConversionTraits
<double, eDefault
>
313 : PrimitiveConversionTraits_float
{};
315 template <typename T
, ConversionBehavior B
, typename U
>
316 bool ValueToPrimitive(U
& cx
, JS::Handle
<JS::Value
> v
,
317 const char* sourceDescription
, T
* retval
) {
318 typename PrimitiveConversionTraits
<T
, B
>::jstype t
;
319 if (!PrimitiveConversionTraits
<T
, B
>::converter(cx
, v
, sourceDescription
, &t
))
322 *retval
= static_cast<T
>(
323 static_cast<typename PrimitiveConversionTraits
<T
, B
>::intermediateType
>(
328 } // namespace mozilla::dom
330 #endif /* mozilla_dom_PrimitiveConversions_h */