Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / bindings / PrimitiveConversions.h
blob1c5b62ec8fdbb4a27be2d784a205c9f8de9453ad
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/. */
7 /**
8 * Conversions from jsval to primitive values
9 */
11 #ifndef mozilla_dom_PrimitiveConversions_h
12 #define mozilla_dom_PrimitiveConversions_h
14 #include <limits>
15 #include <math.h>
16 #include <stdint.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 {
26 template <typename T>
27 struct TypeName {};
29 template <>
30 struct TypeName<int8_t> {
31 static const char* value() { return "byte"; }
33 template <>
34 struct TypeName<uint8_t> {
35 static const char* value() { return "octet"; }
37 template <>
38 struct TypeName<int16_t> {
39 static const char* value() { return "short"; }
41 template <>
42 struct TypeName<uint16_t> {
43 static const char* value() { return "unsigned short"; }
45 template <>
46 struct TypeName<int32_t> {
47 static const char* value() { return "long"; }
49 template <>
50 struct TypeName<uint32_t> {
51 static const char* value() { return "unsigned long"; }
53 template <>
54 struct TypeName<int64_t> {
55 static const char* value() { return "long long"; }
57 template <>
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 {};
67 template <typename T>
68 struct DisallowedConversion {
69 typedef int jstype;
70 typedef int intermediateType;
72 private:
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
100 // will become 0.
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);
115 template <>
116 struct PrimitiveConversionTraits<int8_t, eDefault>
117 : PrimitiveConversionTraits_smallInt {
118 typedef uint8_t intermediateType;
120 template <>
121 struct PrimitiveConversionTraits<uint8_t, eDefault>
122 : PrimitiveConversionTraits_smallInt {};
123 template <>
124 struct PrimitiveConversionTraits<int16_t, eDefault>
125 : PrimitiveConversionTraits_smallInt {
126 typedef uint16_t intermediateType;
128 template <>
129 struct PrimitiveConversionTraits<uint16_t, eDefault>
130 : PrimitiveConversionTraits_smallInt {};
131 template <>
132 struct PrimitiveConversionTraits<int32_t, eDefault>
133 : PrimitiveConversionTraits_smallInt {};
134 template <>
135 struct PrimitiveConversionTraits<uint32_t, eDefault>
136 : PrimitiveConversionTraits_smallInt {};
138 template <>
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);
148 template <>
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(); }
164 template <>
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; }
170 template <>
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,
178 T* retval)>
179 struct PrimitiveConversionTraits_ToCheckedIntHelper {
180 typedef T jstype;
181 typedef T intermediateType;
183 static inline bool converter(U cx, JS::Handle<JS::Value> v,
184 const char* sourceDescription, jstype* retval) {
185 double intermediate;
186 if (!JS::ToNumber(cx, v, &intermediate)) {
187 return false;
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,
197 T* retval) {
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());
206 bool neg = (d < 0);
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);
216 return true;
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!");
232 if (std::isnan(d)) {
233 *retval = 0;
234 return true;
236 if (d >= PrimitiveConversionTraits_Limits<T>::max()) {
237 *retval = PrimitiveConversionTraits_Limits<T>::max();
238 return true;
240 if (d <= PrimitiveConversionTraits_Limits<T>::min()) {
241 *retval = PrimitiveConversionTraits_Limits<T>::min();
242 return true;
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.
264 truncated &= ~1;
267 *retval = truncated;
268 return true;
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> {
280 template <>
281 struct PrimitiveConversionTraits<bool, eDefault> {
282 typedef bool jstype;
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);
287 return true;
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);
308 template <>
309 struct PrimitiveConversionTraits<float, eDefault>
310 : PrimitiveConversionTraits_float {};
311 template <>
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))
320 return false;
322 *retval = static_cast<T>(
323 static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(
324 t));
325 return true;
328 } // namespace mozilla::dom
330 #endif /* mozilla_dom_PrimitiveConversions_h */