tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */