1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bindings/PrimitiveConversions.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,358 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 1.5 +/* vim: set ts=2 sw=2 et tw=79: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/** 1.11 + * Conversions from jsval to primitive values 1.12 + */ 1.13 + 1.14 +#ifndef mozilla_dom_PrimitiveConversions_h 1.15 +#define mozilla_dom_PrimitiveConversions_h 1.16 + 1.17 +#include <limits> 1.18 +#include <math.h> 1.19 +#include <stdint.h> 1.20 + 1.21 +#include "jsapi.h" 1.22 +#include "mozilla/Assertions.h" 1.23 +#include "mozilla/ErrorResult.h" 1.24 +#include "mozilla/FloatingPoint.h" 1.25 + 1.26 +namespace mozilla { 1.27 +namespace dom { 1.28 + 1.29 +template<typename T> 1.30 +struct TypeName { 1.31 +}; 1.32 + 1.33 +template<> 1.34 +struct TypeName<int8_t> { 1.35 + static const char* value() { 1.36 + return "byte"; 1.37 + } 1.38 +}; 1.39 +template<> 1.40 +struct TypeName<uint8_t> { 1.41 + static const char* value() { 1.42 + return "octet"; 1.43 + } 1.44 +}; 1.45 +template<> 1.46 +struct TypeName<int16_t> { 1.47 + static const char* value() { 1.48 + return "short"; 1.49 + } 1.50 +}; 1.51 +template<> 1.52 +struct TypeName<uint16_t> { 1.53 + static const char* value() { 1.54 + return "unsigned short"; 1.55 + } 1.56 +}; 1.57 +template<> 1.58 +struct TypeName<int32_t> { 1.59 + static const char* value() { 1.60 + return "long"; 1.61 + } 1.62 +}; 1.63 +template<> 1.64 +struct TypeName<uint32_t> { 1.65 + static const char* value() { 1.66 + return "unsigned long"; 1.67 + } 1.68 +}; 1.69 +template<> 1.70 +struct TypeName<int64_t> { 1.71 + static const char* value() { 1.72 + return "long long"; 1.73 + } 1.74 +}; 1.75 +template<> 1.76 +struct TypeName<uint64_t> { 1.77 + static const char* value() { 1.78 + return "unsigned long long"; 1.79 + } 1.80 +}; 1.81 + 1.82 + 1.83 +enum ConversionBehavior { 1.84 + eDefault, 1.85 + eEnforceRange, 1.86 + eClamp 1.87 +}; 1.88 + 1.89 +template<typename T, ConversionBehavior B> 1.90 +struct PrimitiveConversionTraits { 1.91 +}; 1.92 + 1.93 +template<typename T> 1.94 +struct DisallowedConversion { 1.95 + typedef int jstype; 1.96 + typedef int intermediateType; 1.97 + 1.98 +private: 1.99 + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, 1.100 + jstype* retval) { 1.101 + MOZ_CRASH("This should never be instantiated!"); 1.102 + } 1.103 +}; 1.104 + 1.105 +struct PrimitiveConversionTraits_smallInt { 1.106 + // The output of JS::ToInt32 is determined as follows: 1.107 + // 1) The value is converted to a double 1.108 + // 2) Anything that's not a finite double returns 0 1.109 + // 3) The double is rounded towards zero to the nearest integer 1.110 + // 4) The resulting integer is reduced mod 2^32. The output of this 1.111 + // operation is an integer in the range [0, 2^32). 1.112 + // 5) If the resulting number is >= 2^31, 2^32 is subtracted from it. 1.113 + // 1.114 + // The result of all this is a number in the range [-2^31, 2^31) 1.115 + // 1.116 + // WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types 1.117 + // are defined in the same way, except that step 4 uses reduction mod 1.118 + // 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5 1.119 + // is only done for the signed types. 1.120 + // 1.121 + // C/C++ define integer conversion semantics to unsigned types as taking 1.122 + // your input integer mod (1 + largest value representable in the 1.123 + // unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32, 1.124 + // converting to the unsigned int of the relevant width will correctly 1.125 + // perform step 4; in particular, the 2^32 possibly subtracted in step 5 1.126 + // will become 0. 1.127 + // 1.128 + // Once we have step 4 done, we're just going to assume 2s-complement 1.129 + // representation and cast directly to the type we really want. 1.130 + // 1.131 + // So we can cast directly for all unsigned types and for int32_t; for 1.132 + // the smaller-width signed types we need to cast through the 1.133 + // corresponding unsigned type. 1.134 + typedef int32_t jstype; 1.135 + typedef int32_t intermediateType; 1.136 + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, 1.137 + jstype* retval) { 1.138 + return JS::ToInt32(cx, v, retval); 1.139 + } 1.140 +}; 1.141 +template<> 1.142 +struct PrimitiveConversionTraits<int8_t, eDefault> : PrimitiveConversionTraits_smallInt { 1.143 + typedef uint8_t intermediateType; 1.144 +}; 1.145 +template<> 1.146 +struct PrimitiveConversionTraits<uint8_t, eDefault> : PrimitiveConversionTraits_smallInt { 1.147 +}; 1.148 +template<> 1.149 +struct PrimitiveConversionTraits<int16_t, eDefault> : PrimitiveConversionTraits_smallInt { 1.150 + typedef uint16_t intermediateType; 1.151 +}; 1.152 +template<> 1.153 +struct PrimitiveConversionTraits<uint16_t, eDefault> : PrimitiveConversionTraits_smallInt { 1.154 +}; 1.155 +template<> 1.156 +struct PrimitiveConversionTraits<int32_t, eDefault> : PrimitiveConversionTraits_smallInt { 1.157 +}; 1.158 +template<> 1.159 +struct PrimitiveConversionTraits<uint32_t, eDefault> : PrimitiveConversionTraits_smallInt { 1.160 +}; 1.161 + 1.162 +template<> 1.163 +struct PrimitiveConversionTraits<int64_t, eDefault> { 1.164 + typedef int64_t jstype; 1.165 + typedef int64_t intermediateType; 1.166 + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, 1.167 + jstype* retval) { 1.168 + return JS::ToInt64(cx, v, retval); 1.169 + } 1.170 +}; 1.171 + 1.172 +template<> 1.173 +struct PrimitiveConversionTraits<uint64_t, eDefault> { 1.174 + typedef uint64_t jstype; 1.175 + typedef uint64_t intermediateType; 1.176 + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, 1.177 + jstype* retval) { 1.178 + return JS::ToUint64(cx, v, retval); 1.179 + } 1.180 +}; 1.181 + 1.182 +template<typename T> 1.183 +struct PrimitiveConversionTraits_Limits { 1.184 + static inline T min() { 1.185 + return std::numeric_limits<T>::min(); 1.186 + } 1.187 + static inline T max() { 1.188 + return std::numeric_limits<T>::max(); 1.189 + } 1.190 +}; 1.191 + 1.192 +template<> 1.193 +struct PrimitiveConversionTraits_Limits<int64_t> { 1.194 + static inline int64_t min() { 1.195 + return -(1LL << 53); 1.196 + } 1.197 + static inline int64_t max() { 1.198 + return (1LL << 53); 1.199 + } 1.200 +}; 1.201 + 1.202 +template<> 1.203 +struct PrimitiveConversionTraits_Limits<uint64_t> { 1.204 + static inline uint64_t min() { 1.205 + return 0; 1.206 + } 1.207 + static inline uint64_t max() { 1.208 + return (1LL << 53); 1.209 + } 1.210 +}; 1.211 + 1.212 +template<typename T, bool (*Enforce)(JSContext* cx, const double& d, T* retval)> 1.213 +struct PrimitiveConversionTraits_ToCheckedIntHelper { 1.214 + typedef T jstype; 1.215 + typedef T intermediateType; 1.216 + 1.217 + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, 1.218 + jstype* retval) { 1.219 + double intermediate; 1.220 + if (!JS::ToNumber(cx, v, &intermediate)) { 1.221 + return false; 1.222 + } 1.223 + 1.224 + return Enforce(cx, intermediate, retval); 1.225 + } 1.226 +}; 1.227 + 1.228 +template<typename T> 1.229 +inline bool 1.230 +PrimitiveConversionTraits_EnforceRange(JSContext* cx, const double& d, T* retval) 1.231 +{ 1.232 + static_assert(std::numeric_limits<T>::is_integer, 1.233 + "This can only be applied to integers!"); 1.234 + 1.235 + if (!mozilla::IsFinite(d)) { 1.236 + return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_NON_FINITE, TypeName<T>::value()); 1.237 + } 1.238 + 1.239 + bool neg = (d < 0); 1.240 + double rounded = floor(neg ? -d : d); 1.241 + rounded = neg ? -rounded : rounded; 1.242 + if (rounded < PrimitiveConversionTraits_Limits<T>::min() || 1.243 + rounded > PrimitiveConversionTraits_Limits<T>::max()) { 1.244 + return ThrowErrorMessage(cx, MSG_ENFORCE_RANGE_OUT_OF_RANGE, TypeName<T>::value()); 1.245 + } 1.246 + 1.247 + *retval = static_cast<T>(rounded); 1.248 + return true; 1.249 +} 1.250 + 1.251 +template<typename T> 1.252 +struct PrimitiveConversionTraits<T, eEnforceRange> : 1.253 + public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_EnforceRange<T> > { 1.254 +}; 1.255 + 1.256 +template<typename T> 1.257 +inline bool 1.258 +PrimitiveConversionTraits_Clamp(JSContext* cx, const double& d, T* retval) 1.259 +{ 1.260 + static_assert(std::numeric_limits<T>::is_integer, 1.261 + "This can only be applied to integers!"); 1.262 + 1.263 + if (mozilla::IsNaN(d)) { 1.264 + *retval = 0; 1.265 + return true; 1.266 + } 1.267 + if (d >= PrimitiveConversionTraits_Limits<T>::max()) { 1.268 + *retval = PrimitiveConversionTraits_Limits<T>::max(); 1.269 + return true; 1.270 + } 1.271 + if (d <= PrimitiveConversionTraits_Limits<T>::min()) { 1.272 + *retval = PrimitiveConversionTraits_Limits<T>::min(); 1.273 + return true; 1.274 + } 1.275 + 1.276 + MOZ_ASSERT(mozilla::IsFinite(d)); 1.277 + 1.278 + // Banker's rounding (round ties towards even). 1.279 + // We move away from 0 by 0.5f and then truncate. That gets us the right 1.280 + // answer for any starting value except plus or minus N.5. With a starting 1.281 + // value of that form, we now have plus or minus N+1. If N is odd, this is 1.282 + // the correct result. If N is even, plus or minus N is the correct result. 1.283 + double toTruncate = (d < 0) ? d - 0.5 : d + 0.5; 1.284 + 1.285 + T truncated = static_cast<T>(toTruncate); 1.286 + 1.287 + if (truncated == toTruncate) { 1.288 + /* 1.289 + * It was a tie (since moving away from 0 by 0.5 gave us the exact integer 1.290 + * we want). Since we rounded away from 0, we either already have an even 1.291 + * number or we have an odd number but the number we want is one closer to 1.292 + * 0. So just unconditionally masking out the ones bit should do the trick 1.293 + * to get us the value we want. 1.294 + */ 1.295 + truncated &= ~1; 1.296 + } 1.297 + 1.298 + *retval = truncated; 1.299 + return true; 1.300 +} 1.301 + 1.302 +template<typename T> 1.303 +struct PrimitiveConversionTraits<T, eClamp> : 1.304 + public PrimitiveConversionTraits_ToCheckedIntHelper<T, PrimitiveConversionTraits_Clamp<T> > { 1.305 +}; 1.306 + 1.307 + 1.308 +template<ConversionBehavior B> 1.309 +struct PrimitiveConversionTraits<bool, B> : public DisallowedConversion<bool> {}; 1.310 + 1.311 +template<> 1.312 +struct PrimitiveConversionTraits<bool, eDefault> { 1.313 + typedef bool jstype; 1.314 + typedef bool intermediateType; 1.315 + static inline bool converter(JSContext* /* unused */, JS::Handle<JS::Value> v, 1.316 + jstype* retval) { 1.317 + *retval = JS::ToBoolean(v); 1.318 + return true; 1.319 + } 1.320 +}; 1.321 + 1.322 + 1.323 +template<ConversionBehavior B> 1.324 +struct PrimitiveConversionTraits<float, B> : public DisallowedConversion<float> {}; 1.325 + 1.326 +template<ConversionBehavior B> 1.327 +struct PrimitiveConversionTraits<double, B> : public DisallowedConversion<double> {}; 1.328 + 1.329 +struct PrimitiveConversionTraits_float { 1.330 + typedef double jstype; 1.331 + typedef double intermediateType; 1.332 + static inline bool converter(JSContext* cx, JS::Handle<JS::Value> v, 1.333 + jstype* retval) { 1.334 + return JS::ToNumber(cx, v, retval); 1.335 + } 1.336 +}; 1.337 + 1.338 +template<> 1.339 +struct PrimitiveConversionTraits<float, eDefault> : PrimitiveConversionTraits_float { 1.340 +}; 1.341 +template<> 1.342 +struct PrimitiveConversionTraits<double, eDefault> : PrimitiveConversionTraits_float { 1.343 +}; 1.344 + 1.345 + 1.346 +template<typename T, ConversionBehavior B> 1.347 +bool ValueToPrimitive(JSContext* cx, JS::Handle<JS::Value> v, T* retval) 1.348 +{ 1.349 + typename PrimitiveConversionTraits<T, B>::jstype t; 1.350 + if (!PrimitiveConversionTraits<T, B>::converter(cx, v, &t)) 1.351 + return false; 1.352 + 1.353 + *retval = static_cast<T>( 1.354 + static_cast<typename PrimitiveConversionTraits<T, B>::intermediateType>(t)); 1.355 + return true; 1.356 +} 1.357 + 1.358 +} // namespace dom 1.359 +} // namespace mozilla 1.360 + 1.361 +#endif /* mozilla_dom_PrimitiveConversions_h */