PrimitiveConversions.h (10865B)
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/. */ 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 <math.h> 15 #include <stdint.h> 16 17 #include <limits> 18 19 #include "js/Conversions.h" 20 #include "js/RootingAPI.h" 21 #include "mozilla/Assertions.h" 22 #include "mozilla/dom/BindingCallContext.h" 23 24 namespace mozilla::dom { 25 26 template <typename T> 27 struct TypeName {}; 28 29 template <> 30 struct TypeName<int8_t> { 31 static const char* value() { return "byte"; } 32 }; 33 template <> 34 struct TypeName<uint8_t> { 35 static const char* value() { return "octet"; } 36 }; 37 template <> 38 struct TypeName<int16_t> { 39 static const char* value() { return "short"; } 40 }; 41 template <> 42 struct TypeName<uint16_t> { 43 static const char* value() { return "unsigned short"; } 44 }; 45 template <> 46 struct TypeName<int32_t> { 47 static const char* value() { return "long"; } 48 }; 49 template <> 50 struct TypeName<uint32_t> { 51 static const char* value() { return "unsigned long"; } 52 }; 53 template <> 54 struct TypeName<int64_t> { 55 static const char* value() { return "long long"; } 56 }; 57 template <> 58 struct TypeName<uint64_t> { 59 static const char* value() { return "unsigned long long"; } 60 }; 61 62 enum ConversionBehavior { eDefault, eEnforceRange, eClamp }; 63 64 template <typename T, ConversionBehavior B> 65 struct PrimitiveConversionTraits {}; 66 67 template <typename T> 68 struct DisallowedConversion { 69 typedef int jstype; 70 typedef int intermediateType; 71 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!"); 76 } 77 }; 78 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. 87 // 88 // The result of all this is a number in the range [-2^31, 2^31) 89 // 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. 94 // 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. 101 // 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. 104 // 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); 113 } 114 }; 115 template <> 116 struct PrimitiveConversionTraits<int8_t, eDefault> 117 : PrimitiveConversionTraits_smallInt { 118 typedef uint8_t intermediateType; 119 }; 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; 127 }; 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 {}; 137 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); 145 } 146 }; 147 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); 155 } 156 }; 157 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(); } 162 }; 163 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; } 168 }; 169 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; } 174 }; 175 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; 182 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; 188 } 189 190 return Enforce(cx, sourceDescription, intermediate, retval); 191 } 192 }; 193 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!"); 200 201 if (!std::isfinite(d)) { 202 return cx.ThrowErrorMessage<MSG_ENFORCE_RANGE_NON_FINITE>( 203 sourceDescription, TypeName<T>::value()); 204 } 205 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()); 213 } 214 215 *retval = static_cast<T>(rounded); 216 return true; 217 } 218 219 template <typename T> 220 struct PrimitiveConversionTraits<T, eEnforceRange> 221 : public PrimitiveConversionTraits_ToCheckedIntHelper< 222 T, BindingCallContext&, PrimitiveConversionTraits_EnforceRange<T> > { 223 }; 224 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!"); 231 232 if (std::isnan(d)) { 233 *retval = 0; 234 return true; 235 } 236 if (d >= PrimitiveConversionTraits_Limits<T>::max()) { 237 *retval = PrimitiveConversionTraits_Limits<T>::max(); 238 return true; 239 } 240 if (d <= PrimitiveConversionTraits_Limits<T>::min()) { 241 *retval = PrimitiveConversionTraits_Limits<T>::min(); 242 return true; 243 } 244 245 MOZ_ASSERT(std::isfinite(d)); 246 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; 253 254 T truncated = static_cast<T>(toTruncate); 255 256 if (truncated == toTruncate) { 257 /* 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. 263 */ 264 truncated &= ~1; 265 } 266 267 *retval = truncated; 268 return true; 269 } 270 271 template <typename T> 272 struct PrimitiveConversionTraits<T, eClamp> 273 : public PrimitiveConversionTraits_ToCheckedIntHelper< 274 T, JSContext*, PrimitiveConversionTraits_Clamp<T> > {}; 275 276 template <ConversionBehavior B> 277 struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> { 278 }; 279 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; 288 } 289 }; 290 291 template <ConversionBehavior B> 292 struct PrimitiveConversionTraits<float, B> 293 : public DisallowedConversion<float> {}; 294 295 template <ConversionBehavior B> 296 struct PrimitiveConversionTraits<double, B> 297 : public DisallowedConversion<double> {}; 298 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); 305 } 306 }; 307 308 template <> 309 struct PrimitiveConversionTraits<float, eDefault> 310 : PrimitiveConversionTraits_float {}; 311 template <> 312 struct PrimitiveConversionTraits<double, eDefault> 313 : PrimitiveConversionTraits_float {}; 314 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; 321 322 *retval = static_cast<T>( 323 static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>( 324 t)); 325 return true; 326 } 327 328 } // namespace mozilla::dom 329 330 #endif /* mozilla_dom_PrimitiveConversions_h */