dom/bindings/PrimitiveConversions.h

changeset 0
6474c204b198
     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 */

mercurial