tor-browser

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

jsmath.cpp (34733B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * JS math package.
      9 */
     10 
     11 #include "jsmath.h"
     12 
     13 #include "mozilla/CheckedInt.h"
     14 #include "mozilla/FloatingPoint.h"
     15 #include "mozilla/MathAlgorithms.h"
     16 #include "mozilla/RandomNum.h"
     17 #include "mozilla/WrappingOperations.h"
     18 
     19 #include <cmath>
     20 
     21 #include "fdlibm.h"
     22 #include "jsapi.h"
     23 #include "jstypes.h"
     24 
     25 #include "jit/InlinableNatives.h"
     26 #include "js/Class.h"
     27 #include "js/ForOfIterator.h"
     28 #include "js/Prefs.h"
     29 #include "js/PropertySpec.h"
     30 #include "util/DifferentialTesting.h"
     31 #include "vm/Float16.h"
     32 #include "vm/Interpreter.h"
     33 #include "vm/JSContext.h"
     34 #include "vm/Realm.h"
     35 #include "vm/Time.h"
     36 #include "xsum/xsum.h"
     37 
     38 #include "vm/JSObject-inl.h"
     39 
     40 using namespace js;
     41 
     42 using JS::GenericNaN;
     43 using JS::ToNumber;
     44 using mozilla::ExponentComponent;
     45 using mozilla::FloatingPoint;
     46 using mozilla::IsNegative;
     47 using mozilla::IsNegativeZero;
     48 using mozilla::Maybe;
     49 using mozilla::NegativeInfinity;
     50 using mozilla::NumberEqualsInt32;
     51 using mozilla::NumberEqualsInt64;
     52 using mozilla::PositiveInfinity;
     53 using mozilla::WrappingMultiply;
     54 
     55 bool js::math_use_fdlibm_for_sin_cos_tan() {
     56  return JS::Prefs::use_fdlibm_for_sin_cos_tan();
     57 }
     58 
     59 static inline bool UseFdlibmForSinCosTan(const CallArgs& args) {
     60  return math_use_fdlibm_for_sin_cos_tan() ||
     61         args.callee().nonCCWRealm()->creationOptions().alwaysUseFdlibm();
     62 }
     63 
     64 // Stack alignment on x86 Windows is 4 byte. Align to 16 bytes when calling
     65 // rounding functions with double parameters.
     66 //
     67 // See |ABIStackAlignment| in "js/src/jit/x86/Assembler-x86.h".
     68 #if defined(JS_CODEGEN_X86) && (!defined(__GNUC__) || defined(__MINGW32__))
     69 #  define ALIGN_STACK_FOR_ROUNDING_FUNCTION \
     70    __attribute__((force_align_arg_pointer))
     71 #else
     72 #  define ALIGN_STACK_FOR_ROUNDING_FUNCTION
     73 #endif
     74 
     75 template <UnaryMathFunctionType F>
     76 static bool math_function(JSContext* cx, CallArgs& args) {
     77  if (args.length() == 0) {
     78    args.rval().setNaN();
     79    return true;
     80  }
     81 
     82  double x;
     83  if (!ToNumber(cx, args[0], &x)) {
     84    return false;
     85  }
     86 
     87  // TODO(post-Warp): Re-evaluate if it's still necessary resp. useful to always
     88  // type the value as a double.
     89 
     90  // NB: Always stored as a double so the math function can be inlined
     91  // through MMathFunction.
     92  double z = F(x);
     93  args.rval().setDouble(z);
     94  return true;
     95 }
     96 
     97 double js::math_abs_impl(double x) { return mozilla::Abs(x); }
     98 
     99 bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
    100  CallArgs args = CallArgsFromVp(argc, vp);
    101 
    102  if (args.length() == 0) {
    103    args.rval().setNaN();
    104    return true;
    105  }
    106 
    107  double x;
    108  if (!ToNumber(cx, args[0], &x)) {
    109    return false;
    110  }
    111 
    112  args.rval().setNumber(math_abs_impl(x));
    113  return true;
    114 }
    115 
    116 double js::math_acos_impl(double x) {
    117  AutoUnsafeCallWithABI unsafe;
    118  return fdlibm_acos(x);
    119 }
    120 
    121 static bool math_acos(JSContext* cx, unsigned argc, Value* vp) {
    122  CallArgs args = CallArgsFromVp(argc, vp);
    123  return math_function<math_acos_impl>(cx, args);
    124 }
    125 
    126 double js::math_asin_impl(double x) {
    127  AutoUnsafeCallWithABI unsafe;
    128  return fdlibm_asin(x);
    129 }
    130 
    131 static bool math_asin(JSContext* cx, unsigned argc, Value* vp) {
    132  CallArgs args = CallArgsFromVp(argc, vp);
    133  return math_function<math_asin_impl>(cx, args);
    134 }
    135 
    136 double js::math_atan_impl(double x) {
    137  AutoUnsafeCallWithABI unsafe;
    138  return fdlibm_atan(x);
    139 }
    140 
    141 static bool math_atan(JSContext* cx, unsigned argc, Value* vp) {
    142  CallArgs args = CallArgsFromVp(argc, vp);
    143  return math_function<math_atan_impl>(cx, args);
    144 }
    145 
    146 double js::ecmaAtan2(double y, double x) {
    147  AutoUnsafeCallWithABI unsafe;
    148  return fdlibm_atan2(y, x);
    149 }
    150 
    151 static bool math_atan2(JSContext* cx, unsigned argc, Value* vp) {
    152  CallArgs args = CallArgsFromVp(argc, vp);
    153 
    154  double y;
    155  if (!ToNumber(cx, args.get(0), &y)) {
    156    return false;
    157  }
    158 
    159  double x;
    160  if (!ToNumber(cx, args.get(1), &x)) {
    161    return false;
    162  }
    163 
    164  double z = ecmaAtan2(y, x);
    165  args.rval().setDouble(z);
    166  return true;
    167 }
    168 
    169 ALIGN_STACK_FOR_ROUNDING_FUNCTION
    170 double js::math_ceil_impl(double x) {
    171  AutoUnsafeCallWithABI unsafe;
    172  return std::ceil(x);
    173 }
    174 
    175 static bool math_ceil(JSContext* cx, unsigned argc, Value* vp) {
    176  CallArgs args = CallArgsFromVp(argc, vp);
    177 
    178  if (args.length() == 0) {
    179    args.rval().setNaN();
    180    return true;
    181  }
    182 
    183  double x;
    184  if (!ToNumber(cx, args[0], &x)) {
    185    return false;
    186  }
    187 
    188  args.rval().setNumber(math_ceil_impl(x));
    189  return true;
    190 }
    191 
    192 static bool math_clz32(JSContext* cx, unsigned argc, Value* vp) {
    193  CallArgs args = CallArgsFromVp(argc, vp);
    194 
    195  if (args.length() == 0) {
    196    args.rval().setInt32(32);
    197    return true;
    198  }
    199 
    200  uint32_t n;
    201  if (!ToUint32(cx, args[0], &n)) {
    202    return false;
    203  }
    204 
    205  if (n == 0) {
    206    args.rval().setInt32(32);
    207    return true;
    208  }
    209 
    210  args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
    211  return true;
    212 }
    213 
    214 double js::math_cos_fdlibm_impl(double x) {
    215  AutoUnsafeCallWithABI unsafe;
    216  return fdlibm_cos(x);
    217 }
    218 
    219 double js::math_cos_native_impl(double x) {
    220  MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan());
    221  AutoUnsafeCallWithABI unsafe;
    222  return std::cos(x);
    223 }
    224 
    225 static bool math_cos(JSContext* cx, unsigned argc, Value* vp) {
    226  CallArgs args = CallArgsFromVp(argc, vp);
    227  if (UseFdlibmForSinCosTan(args)) {
    228    return math_function<math_cos_fdlibm_impl>(cx, args);
    229  }
    230  return math_function<math_cos_native_impl>(cx, args);
    231 }
    232 
    233 double js::math_exp_impl(double x) {
    234  AutoUnsafeCallWithABI unsafe;
    235  return fdlibm_exp(x);
    236 }
    237 
    238 static bool math_exp(JSContext* cx, unsigned argc, Value* vp) {
    239  CallArgs args = CallArgsFromVp(argc, vp);
    240  return math_function<math_exp_impl>(cx, args);
    241 }
    242 
    243 ALIGN_STACK_FOR_ROUNDING_FUNCTION
    244 double js::math_floor_impl(double x) {
    245  AutoUnsafeCallWithABI unsafe;
    246  return std::floor(x);
    247 }
    248 
    249 bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
    250  CallArgs args = CallArgsFromVp(argc, vp);
    251 
    252  if (args.length() == 0) {
    253    args.rval().setNaN();
    254    return true;
    255  }
    256 
    257  double x;
    258  if (!ToNumber(cx, args[0], &x)) {
    259    return false;
    260  }
    261 
    262  args.rval().setNumber(math_floor_impl(x));
    263  return true;
    264 }
    265 
    266 bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
    267                          MutableHandleValue res) {
    268  int32_t a = 0, b = 0;
    269  if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
    270    return false;
    271  }
    272  if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
    273    return false;
    274  }
    275 
    276  res.setInt32(WrappingMultiply(a, b));
    277  return true;
    278 }
    279 
    280 static bool math_imul(JSContext* cx, unsigned argc, Value* vp) {
    281  CallArgs args = CallArgsFromVp(argc, vp);
    282 
    283  return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
    284 }
    285 
    286 // Implements Math.fround (20.2.2.16) up to step 3
    287 bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
    288  double d;
    289  bool success = ToNumber(cx, v, &d);
    290  *out = static_cast<float>(d);
    291  return success;
    292 }
    293 
    294 bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) {
    295  float f;
    296  if (!RoundFloat32(cx, arg, &f)) {
    297    return false;
    298  }
    299 
    300  res.setDouble(static_cast<double>(f));
    301  return true;
    302 }
    303 
    304 double js::RoundFloat32(double d) {
    305  return static_cast<double>(static_cast<float>(d));
    306 }
    307 
    308 static bool math_fround(JSContext* cx, unsigned argc, Value* vp) {
    309  CallArgs args = CallArgsFromVp(argc, vp);
    310 
    311  if (args.length() == 0) {
    312    args.rval().setNaN();
    313    return true;
    314  }
    315 
    316  return RoundFloat32(cx, args[0], args.rval());
    317 }
    318 
    319 double js::RoundFloat16(double d) {
    320  AutoUnsafeCallWithABI unsafe;
    321 
    322  // http://tc39.es/proposal-float16array/#sec-function-properties-of-the-math-object
    323 
    324  // 1. Let n be ? ToNumber(x).
    325  // [Not applicable here]
    326 
    327  // 2. If n is NaN, return NaN.
    328  // 3. If n is one of +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return n.
    329  // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format
    330  // using roundTiesToEven mode.
    331  js::float16 f16 = js::float16(d);
    332 
    333  // 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64
    334  // format.
    335  // 6. Return the ECMAScript Number value corresponding to n64.
    336  return static_cast<double>(f16);
    337 }
    338 
    339 static bool math_f16round(JSContext* cx, unsigned argc, Value* vp) {
    340  // http://tc39.es/proposal-float16array/#sec-function-properties-of-the-math-object
    341  CallArgs args = CallArgsFromVp(argc, vp);
    342 
    343  if (args.length() == 0) {
    344    args.rval().setNaN();
    345    return true;
    346  }
    347 
    348  // 1. Let n be ? ToNumber(x).
    349  double d;
    350  if (!ToNumber(cx, args[0], &d)) {
    351    return false;
    352  }
    353 
    354  // Steps 2-6.
    355  args.rval().setDouble(RoundFloat16(d));
    356  return true;
    357 }
    358 
    359 double js::math_log_impl(double x) {
    360  AutoUnsafeCallWithABI unsafe;
    361  return fdlibm_log(x);
    362 }
    363 
    364 static bool math_log(JSContext* cx, unsigned argc, Value* vp) {
    365  CallArgs args = CallArgsFromVp(argc, vp);
    366  return math_function<math_log_impl>(cx, args);
    367 }
    368 
    369 double js::math_max_impl(double x, double y) {
    370  AutoUnsafeCallWithABI unsafe;
    371 
    372  // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
    373  if (x > y || std::isnan(x) || (x == y && IsNegative(y))) {
    374    return x;
    375  }
    376  return y;
    377 }
    378 
    379 bool js::math_max(JSContext* cx, unsigned argc, Value* vp) {
    380  CallArgs args = CallArgsFromVp(argc, vp);
    381 
    382  double maxval = NegativeInfinity<double>();
    383  for (unsigned i = 0; i < args.length(); i++) {
    384    double x;
    385    if (!ToNumber(cx, args[i], &x)) {
    386      return false;
    387    }
    388    maxval = math_max_impl(x, maxval);
    389  }
    390  args.rval().setNumber(maxval);
    391  return true;
    392 }
    393 
    394 double js::math_min_impl(double x, double y) {
    395  AutoUnsafeCallWithABI unsafe;
    396 
    397  // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
    398  if (x < y || std::isnan(x) || (x == y && IsNegativeZero(x))) {
    399    return x;
    400  }
    401  return y;
    402 }
    403 
    404 bool js::math_min(JSContext* cx, unsigned argc, Value* vp) {
    405  CallArgs args = CallArgsFromVp(argc, vp);
    406 
    407  double minval = PositiveInfinity<double>();
    408  for (unsigned i = 0; i < args.length(); i++) {
    409    double x;
    410    if (!ToNumber(cx, args[i], &x)) {
    411      return false;
    412    }
    413    minval = math_min_impl(x, minval);
    414  }
    415  args.rval().setNumber(minval);
    416  return true;
    417 }
    418 
    419 double js::powi(double x, int32_t y) {
    420  AutoUnsafeCallWithABI unsafe;
    421 
    422  // It's only safe to optimize this when we can compute with integer values or
    423  // the exponent is a small, positive constant.
    424  if (y >= 0) {
    425    uint32_t n = uint32_t(y);
    426 
    427    // NB: Have to take fast-path for n <= 4 to match |MPow::foldsTo|. Otherwise
    428    // we risk causing differential testing issues.
    429    if (n == 0) {
    430      return 1;
    431    }
    432    if (n == 1) {
    433      return x;
    434    }
    435    if (n == 2) {
    436      return x * x;
    437    }
    438    if (n == 3) {
    439      return x * x * x;
    440    }
    441    if (n == 4) {
    442      double z = x * x;
    443      return z * z;
    444    }
    445 
    446    int64_t i;
    447    if (NumberEqualsInt64(x, &i)) {
    448      // Special-case: |-0 ** odd| is -0.
    449      if (i == 0) {
    450        return (n & 1) ? x : 0;
    451      }
    452 
    453      // Use int64 to cover cases like |Math.pow(2, 53)|.
    454      mozilla::CheckedInt64 runningSquare = i;
    455      mozilla::CheckedInt64 result = 1;
    456      while (true) {
    457        if ((n & 1) != 0) {
    458          result *= runningSquare;
    459          if (!result.isValid()) {
    460            break;
    461          }
    462        }
    463 
    464        n >>= 1;
    465        if (n == 0) {
    466          return static_cast<double>(result.value());
    467        }
    468 
    469        runningSquare *= runningSquare;
    470        if (!runningSquare.isValid()) {
    471          break;
    472        }
    473      }
    474    }
    475 
    476    // Fall-back to use std::pow to reduce floating point precision errors.
    477  }
    478 
    479  return std::pow(x, static_cast<double>(y));  // Avoid pow(double, int).
    480 }
    481 
    482 double js::ecmaPow(double x, double y) {
    483  AutoUnsafeCallWithABI unsafe;
    484 
    485  /*
    486   * Use powi if the exponent is an integer-valued double. We don't have to
    487   * check for NaN since a comparison with NaN is always false.
    488   */
    489  int32_t yi;
    490  if (NumberEqualsInt32(y, &yi)) {
    491    return powi(x, yi);
    492  }
    493 
    494  /*
    495   * Because C99 and ECMA specify different behavior for pow(),
    496   * we need to wrap the libm call to make it ECMA compliant.
    497   */
    498  if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
    499    return GenericNaN();
    500  }
    501 
    502  /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
    503  if (y == 0) {
    504    return 1;
    505  }
    506 
    507  /*
    508   * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
    509   * when x = -0.0, so we have to guard for this.
    510   */
    511  if (std::isfinite(x) && x != 0.0) {
    512    if (y == 0.5) {
    513      return std::sqrt(x);
    514    }
    515    if (y == -0.5) {
    516      return 1.0 / std::sqrt(x);
    517    }
    518  }
    519  return std::pow(x, y);
    520 }
    521 
    522 static bool math_pow(JSContext* cx, unsigned argc, Value* vp) {
    523  CallArgs args = CallArgsFromVp(argc, vp);
    524 
    525  double x;
    526  if (!ToNumber(cx, args.get(0), &x)) {
    527    return false;
    528  }
    529 
    530  double y;
    531  if (!ToNumber(cx, args.get(1), &y)) {
    532    return false;
    533  }
    534 
    535  double z = ecmaPow(x, y);
    536  args.rval().setNumber(z);
    537  return true;
    538 }
    539 
    540 uint64_t js::GenerateRandomSeed() {
    541  Maybe<uint64_t> maybeSeed = mozilla::RandomUint64();
    542 
    543  return maybeSeed.valueOrFrom([] {
    544    // Use PRMJ_Now() in case we couldn't read random bits from the OS.
    545    uint64_t timestamp = PRMJ_Now();
    546    return timestamp ^ (timestamp << 32);
    547  });
    548 }
    549 
    550 void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
    551  // XorShift128PlusRNG must be initialized with a non-zero seed.
    552  do {
    553    seed[0] = GenerateRandomSeed();
    554    seed[1] = GenerateRandomSeed();
    555  } while (seed[0] == 0 && seed[1] == 0);
    556 }
    557 
    558 mozilla::non_crypto::XorShift128PlusRNG&
    559 Realm::getOrCreateRandomNumberGenerator() {
    560  if (randomNumberGenerator_.isNothing()) {
    561    mozilla::Array<uint64_t, 2> seed;
    562    GenerateXorShift128PlusSeed(seed);
    563    randomNumberGenerator_.emplace(seed[0], seed[1]);
    564  }
    565 
    566  return randomNumberGenerator_.ref();
    567 }
    568 
    569 double js::math_random_impl(JSContext* cx) {
    570  return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
    571 }
    572 
    573 static bool math_random(JSContext* cx, unsigned argc, Value* vp) {
    574  CallArgs args = CallArgsFromVp(argc, vp);
    575  if (js::SupportDifferentialTesting()) {
    576    args.rval().setDouble(0);
    577  } else {
    578    args.rval().setDouble(math_random_impl(cx));
    579  }
    580  return true;
    581 }
    582 
    583 template <typename T>
    584 T js::GetBiggestNumberLessThan(T x) {
    585  MOZ_ASSERT(!IsNegative(x));
    586  MOZ_ASSERT(std::isfinite(x));
    587  using Bits = typename mozilla::FloatingPoint<T>::Bits;
    588  Bits bits = mozilla::BitwiseCast<Bits>(x);
    589  MOZ_ASSERT(bits > 0, "will underflow");
    590  return mozilla::BitwiseCast<T>(bits - 1);
    591 }
    592 
    593 template double js::GetBiggestNumberLessThan<>(double x);
    594 template float js::GetBiggestNumberLessThan<>(float x);
    595 
    596 ALIGN_STACK_FOR_ROUNDING_FUNCTION
    597 double js::math_round_impl(double x) {
    598  AutoUnsafeCallWithABI unsafe;
    599 
    600  double result = std::ceil(x);
    601  if (x < result - 0.5) {
    602    result -= 1.0;
    603  }
    604  return result;
    605 }
    606 
    607 float js::math_roundf_impl(float x) {
    608  AutoUnsafeCallWithABI unsafe;
    609 
    610  float result = std::ceil(x);
    611  if (x < result - 0.5f) {
    612    result -= 1.0f;
    613  }
    614  return result;
    615 }
    616 
    617 /* ES5 15.8.2.15. */
    618 static bool math_round(JSContext* cx, unsigned argc, Value* vp) {
    619  CallArgs args = CallArgsFromVp(argc, vp);
    620 
    621  if (args.length() == 0) {
    622    args.rval().setNaN();
    623    return true;
    624  }
    625 
    626  double x;
    627  if (!ToNumber(cx, args[0], &x)) {
    628    return false;
    629  }
    630 
    631  args.rval().setNumber(math_round_impl(x));
    632  return true;
    633 }
    634 
    635 double js::math_sin_fdlibm_impl(double x) {
    636  AutoUnsafeCallWithABI unsafe;
    637  return fdlibm_sin(x);
    638 }
    639 
    640 double js::math_sin_native_impl(double x) {
    641  MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan());
    642  AutoUnsafeCallWithABI unsafe;
    643  return std::sin(x);
    644 }
    645 
    646 static bool math_sin(JSContext* cx, unsigned argc, Value* vp) {
    647  CallArgs args = CallArgsFromVp(argc, vp);
    648  if (UseFdlibmForSinCosTan(args)) {
    649    return math_function<math_sin_fdlibm_impl>(cx, args);
    650  }
    651  return math_function<math_sin_native_impl>(cx, args);
    652 }
    653 
    654 double js::math_sqrt_impl(double x) {
    655  AutoUnsafeCallWithABI unsafe;
    656  return std::sqrt(x);
    657 }
    658 
    659 static bool math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
    660  CallArgs args = CallArgsFromVp(argc, vp);
    661  return math_function<math_sqrt_impl>(cx, args);
    662 }
    663 
    664 double js::math_tan_fdlibm_impl(double x) {
    665  AutoUnsafeCallWithABI unsafe;
    666  return fdlibm_tan(x);
    667 }
    668 
    669 double js::math_tan_native_impl(double x) {
    670  MOZ_ASSERT(!math_use_fdlibm_for_sin_cos_tan());
    671  AutoUnsafeCallWithABI unsafe;
    672  return std::tan(x);
    673 }
    674 
    675 static bool math_tan(JSContext* cx, unsigned argc, Value* vp) {
    676  CallArgs args = CallArgsFromVp(argc, vp);
    677  if (UseFdlibmForSinCosTan(args)) {
    678    return math_function<math_tan_fdlibm_impl>(cx, args);
    679  }
    680  return math_function<math_tan_native_impl>(cx, args);
    681 }
    682 
    683 double js::math_log10_impl(double x) {
    684  AutoUnsafeCallWithABI unsafe;
    685  return fdlibm_log10(x);
    686 }
    687 
    688 static bool math_log10(JSContext* cx, unsigned argc, Value* vp) {
    689  CallArgs args = CallArgsFromVp(argc, vp);
    690  return math_function<math_log10_impl>(cx, args);
    691 }
    692 
    693 double js::math_log2_impl(double x) {
    694  AutoUnsafeCallWithABI unsafe;
    695  return fdlibm_log2(x);
    696 }
    697 
    698 static bool math_log2(JSContext* cx, unsigned argc, Value* vp) {
    699  CallArgs args = CallArgsFromVp(argc, vp);
    700  return math_function<math_log2_impl>(cx, args);
    701 }
    702 
    703 double js::math_log1p_impl(double x) {
    704  AutoUnsafeCallWithABI unsafe;
    705  return fdlibm_log1p(x);
    706 }
    707 
    708 static bool math_log1p(JSContext* cx, unsigned argc, Value* vp) {
    709  CallArgs args = CallArgsFromVp(argc, vp);
    710  return math_function<math_log1p_impl>(cx, args);
    711 }
    712 
    713 double js::math_expm1_impl(double x) {
    714  AutoUnsafeCallWithABI unsafe;
    715  return fdlibm_expm1(x);
    716 }
    717 
    718 static bool math_expm1(JSContext* cx, unsigned argc, Value* vp) {
    719  CallArgs args = CallArgsFromVp(argc, vp);
    720  return math_function<math_expm1_impl>(cx, args);
    721 }
    722 
    723 double js::math_cosh_impl(double x) {
    724  AutoUnsafeCallWithABI unsafe;
    725  return fdlibm_cosh(x);
    726 }
    727 
    728 static bool math_cosh(JSContext* cx, unsigned argc, Value* vp) {
    729  CallArgs args = CallArgsFromVp(argc, vp);
    730  return math_function<math_cosh_impl>(cx, args);
    731 }
    732 
    733 double js::math_sinh_impl(double x) {
    734  AutoUnsafeCallWithABI unsafe;
    735  return fdlibm_sinh(x);
    736 }
    737 
    738 static bool math_sinh(JSContext* cx, unsigned argc, Value* vp) {
    739  CallArgs args = CallArgsFromVp(argc, vp);
    740  return math_function<math_sinh_impl>(cx, args);
    741 }
    742 
    743 double js::math_tanh_impl(double x) {
    744  AutoUnsafeCallWithABI unsafe;
    745  return fdlibm_tanh(x);
    746 }
    747 
    748 static bool math_tanh(JSContext* cx, unsigned argc, Value* vp) {
    749  CallArgs args = CallArgsFromVp(argc, vp);
    750  return math_function<math_tanh_impl>(cx, args);
    751 }
    752 
    753 double js::math_acosh_impl(double x) {
    754  AutoUnsafeCallWithABI unsafe;
    755  return fdlibm_acosh(x);
    756 }
    757 
    758 static bool math_acosh(JSContext* cx, unsigned argc, Value* vp) {
    759  CallArgs args = CallArgsFromVp(argc, vp);
    760  return math_function<math_acosh_impl>(cx, args);
    761 }
    762 
    763 double js::math_asinh_impl(double x) {
    764  AutoUnsafeCallWithABI unsafe;
    765  return fdlibm_asinh(x);
    766 }
    767 
    768 static bool math_asinh(JSContext* cx, unsigned argc, Value* vp) {
    769  CallArgs args = CallArgsFromVp(argc, vp);
    770  return math_function<math_asinh_impl>(cx, args);
    771 }
    772 
    773 double js::math_atanh_impl(double x) {
    774  AutoUnsafeCallWithABI unsafe;
    775  return fdlibm_atanh(x);
    776 }
    777 
    778 static bool math_atanh(JSContext* cx, unsigned argc, Value* vp) {
    779  CallArgs args = CallArgsFromVp(argc, vp);
    780  return math_function<math_atanh_impl>(cx, args);
    781 }
    782 
    783 double js::ecmaHypot(double x, double y) {
    784  AutoUnsafeCallWithABI unsafe;
    785  return fdlibm_hypot(x, y);
    786 }
    787 
    788 static inline void hypot_step(double& scale, double& sumsq, double x) {
    789  double xabs = mozilla::Abs(x);
    790  if (scale < xabs) {
    791    sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
    792    scale = xabs;
    793  } else if (scale != 0) {
    794    sumsq += (xabs / scale) * (xabs / scale);
    795  }
    796 }
    797 
    798 double js::hypot4(double x, double y, double z, double w) {
    799  AutoUnsafeCallWithABI unsafe;
    800 
    801  // Check for infinities or NaNs so that we can return immediately.
    802  if (std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w)) {
    803    return mozilla::PositiveInfinity<double>();
    804  }
    805 
    806  if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w)) {
    807    return GenericNaN();
    808  }
    809 
    810  double scale = 0;
    811  double sumsq = 1;
    812 
    813  hypot_step(scale, sumsq, x);
    814  hypot_step(scale, sumsq, y);
    815  hypot_step(scale, sumsq, z);
    816  hypot_step(scale, sumsq, w);
    817 
    818  return scale * std::sqrt(sumsq);
    819 }
    820 
    821 double js::hypot3(double x, double y, double z) {
    822  AutoUnsafeCallWithABI unsafe;
    823  return hypot4(x, y, z, 0.0);
    824 }
    825 
    826 static bool math_hypot(JSContext* cx, unsigned argc, Value* vp) {
    827  CallArgs args = CallArgsFromVp(argc, vp);
    828  return math_hypot_handle(cx, args, args.rval());
    829 }
    830 
    831 bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
    832                           MutableHandleValue res) {
    833  // IonMonkey calls the ecmaHypot function directly if two arguments are
    834  // given. Do that here as well to get the same results.
    835  if (args.length() == 2) {
    836    double x, y;
    837    if (!ToNumber(cx, args[0], &x)) {
    838      return false;
    839    }
    840    if (!ToNumber(cx, args[1], &y)) {
    841      return false;
    842    }
    843 
    844    double result = ecmaHypot(x, y);
    845    res.setDouble(result);
    846    return true;
    847  }
    848 
    849  bool isInfinite = false;
    850  bool isNaN = false;
    851 
    852  double scale = 0;
    853  double sumsq = 1;
    854 
    855  for (unsigned i = 0; i < args.length(); i++) {
    856    double x;
    857    if (!ToNumber(cx, args[i], &x)) {
    858      return false;
    859    }
    860 
    861    isInfinite |= std::isinf(x);
    862    isNaN |= std::isnan(x);
    863    if (isInfinite || isNaN) {
    864      continue;
    865    }
    866 
    867    hypot_step(scale, sumsq, x);
    868  }
    869 
    870  double result = isInfinite ? PositiveInfinity<double>()
    871                  : isNaN    ? GenericNaN()
    872                             : scale * std::sqrt(sumsq);
    873  res.setDouble(result);
    874  return true;
    875 }
    876 
    877 ALIGN_STACK_FOR_ROUNDING_FUNCTION
    878 double js::math_trunc_impl(double x) {
    879  AutoUnsafeCallWithABI unsafe;
    880  return std::trunc(x);
    881 }
    882 
    883 bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
    884  CallArgs args = CallArgsFromVp(argc, vp);
    885  if (args.length() == 0) {
    886    args.rval().setNaN();
    887    return true;
    888  }
    889 
    890  double x;
    891  if (!ToNumber(cx, args[0], &x)) {
    892    return false;
    893  }
    894 
    895  args.rval().setNumber(math_trunc_impl(x));
    896  return true;
    897 }
    898 
    899 double js::math_sign_impl(double x) {
    900  AutoUnsafeCallWithABI unsafe;
    901 
    902  if (std::isnan(x)) {
    903    return GenericNaN();
    904  }
    905 
    906  return x == 0 ? x : x < 0 ? -1 : 1;
    907 }
    908 
    909 bool js::math_sign(JSContext* cx, unsigned argc, Value* vp) {
    910  CallArgs args = CallArgsFromVp(argc, vp);
    911  if (args.length() == 0) {
    912    args.rval().setNaN();
    913    return true;
    914  }
    915 
    916  double x;
    917  if (!ToNumber(cx, args[0], &x)) {
    918    return false;
    919  }
    920 
    921  args.rval().setNumber(math_sign_impl(x));
    922  return true;
    923 }
    924 
    925 double js::math_cbrt_impl(double x) {
    926  AutoUnsafeCallWithABI unsafe;
    927  return fdlibm_cbrt(x);
    928 }
    929 
    930 static bool math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
    931  CallArgs args = CallArgsFromVp(argc, vp);
    932  return math_function<math_cbrt_impl>(cx, args);
    933 }
    934 
    935 static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
    936  CallArgs args = CallArgsFromVp(argc, vp);
    937  args.rval().setString(cx->names().Math);
    938  return true;
    939 }
    940 
    941 enum class SumPreciseState : uint8_t {
    942  MinusZero,
    943  Finite,
    944  PlusInfinity,
    945  MinusInfinity,
    946  NotANumber,
    947 };
    948 
    949 /**
    950 * Math.sumPrecise ( items )
    951 *
    952 * https://tc39.es/proposal-math-sum/#sec-math.sumprecise
    953 */
    954 static bool math_sumPrecise(JSContext* cx, unsigned argc, Value* vp) {
    955  constexpr int64_t MaxCount = int64_t(1) << 53;
    956 
    957  // Step 1. Perform ? RequireObjectCoercible(items).
    958  CallArgs args = CallArgsFromVp(argc, vp);
    959  if (!args.requireAtLeast(cx, "Math.sumPrecise", 1)) {
    960    return false;
    961  }
    962 
    963  // Step 2. Let iteratorRecord be ? GetIterator(items, sync).
    964  JS::ForOfIterator iterator(cx);
    965  if (!iterator.init(args[0], JS::ForOfIterator::ThrowOnNonIterable)) {
    966    return false;
    967  }
    968 
    969  // Step 3. Let state be minus-zero.
    970  SumPreciseState state = SumPreciseState::MinusZero;
    971 
    972  // Step 4. Let sum be 0.
    973  xsum_small_accumulator sum;
    974  xsum_small_init(&sum);
    975 
    976  // Step 5. Let count be 0.
    977  int64_t count = 0;
    978 
    979  // Step 6. Let next be not-started.
    980  // (implicit)
    981 
    982  JS::Rooted<JS::Value> value(cx);
    983 
    984  // Step 7. Repeat, while next is not done,
    985  while (true) {
    986    // Step 7.a. Set next to ? IteratorStepValue(iteratorRecord).
    987    bool done;
    988    if (!iterator.next(&value, &done)) {
    989      return false;
    990    }
    991 
    992    // Step 7.b. If next is not done, then
    993    if (done) {
    994      break;
    995    }
    996 
    997    // Step 7.b.i. Set count to count + 1.
    998    count += 1;
    999 
   1000    // Step 7.b.ii. If count ≥ 2**53, then
   1001    if (count >= MaxCount) {
   1002      // Step 7.b.ii.1. Let error be ThrowCompletion(a newly created RangeError
   1003      // object).
   1004      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1005                                JSMSG_SUMPRECISE_TOO_MANY_VALUES);
   1006 
   1007      // Step 7.b.ii.2. Return ? IteratorClose(iteratorRecord, error).
   1008      iterator.closeThrow();
   1009      return false;
   1010    }
   1011 
   1012    // Step 7.b.iv. If next is not a Number, then
   1013    if (!value.isNumber()) {
   1014      // Step 7.b.iv.1. Let error be ThrowCompletion(a newly created TypeError
   1015      // object).
   1016      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1017                                JSMSG_SUMPRECISE_EXPECTED_NUMBER);
   1018 
   1019      // Step 7.b.iv.2. Return ? IteratorClose(iteratorRecord, error).
   1020      iterator.closeThrow();
   1021      return false;
   1022    }
   1023 
   1024    // Step 7.b.v. Let n be next.
   1025    double n = value.toNumber();
   1026 
   1027    // Step 7.b.vi. If state is not not-a-number, then
   1028    if (state == SumPreciseState::NotANumber) {
   1029      continue;
   1030    }
   1031 
   1032    // Step 7.b.vi.1. If n is NaN, then
   1033    if (std::isnan(n)) {
   1034      // Step 7.b.vi.1.a. Set state to not-a-number.
   1035      state = SumPreciseState::NotANumber;
   1036    } else if (n == PositiveInfinity<double>()) {
   1037      // Step 7.b.vi.2. Else if n is +∞𝔽, then
   1038      if (state == SumPreciseState::MinusInfinity) {
   1039        // Step 7.b.vi.2.a. If state is minus-infinity, set state to
   1040        //                  not-a-number.
   1041        state = SumPreciseState::NotANumber;
   1042      } else {
   1043        // Step 7.b.vi.2.b. Else, set state to plus-infinity.
   1044        state = SumPreciseState::PlusInfinity;
   1045      }
   1046    } else if (n == NegativeInfinity<double>()) {
   1047      // Step 7.b.vi.3. Else if n is -∞𝔽, then
   1048      if (state == SumPreciseState::PlusInfinity) {
   1049        // Step 7.b.vi.3.a. If state is plus-infinity, set state to
   1050        //                  not-a-number.
   1051        state = SumPreciseState::NotANumber;
   1052      } else {
   1053        // Step 7.b.vi.3.b. Else, set state to minus-infinity.
   1054        state = SumPreciseState::MinusInfinity;
   1055      }
   1056    } else if (!IsNegativeZero(n) && (state == SumPreciseState::MinusZero ||
   1057                                      state == SumPreciseState::Finite)) {
   1058      // Step 7.b.vi.4. Else if n is not -0𝔽 and state is either minus-zero or
   1059      //                finite, then
   1060      // Step 7.b.vi.4.a. Set state to finite.
   1061      state = SumPreciseState::Finite;
   1062 
   1063      // Step 7.b.vi.4.b. Set sum to sum + ℝ(n).
   1064      xsum_small_add1(&sum, n);
   1065    }
   1066  }
   1067 
   1068  double rval;
   1069  switch (state) {
   1070    case SumPreciseState::NotANumber:
   1071      // Step 8. If state is not-a-number, return NaN.
   1072      rval = GenericNaN();
   1073      break;
   1074    case SumPreciseState::PlusInfinity:
   1075      // Step 9. If state is plus-infinity, return +∞𝔽.
   1076      rval = PositiveInfinity<double>();
   1077      break;
   1078    case SumPreciseState::MinusInfinity:
   1079      // Step 10. If state is minus-infinity, return -∞𝔽.
   1080      rval = NegativeInfinity<double>();
   1081      break;
   1082    case SumPreciseState::MinusZero:
   1083      // Step 11. If state is minus-zero, return -0𝔽.
   1084      rval = -0.0;
   1085      break;
   1086    case SumPreciseState::Finite:
   1087      // Step 12. Return 𝔽(sum).
   1088      rval = xsum_small_round(&sum);
   1089      break;
   1090  }
   1091 
   1092  args.rval().setNumber(rval);
   1093  return true;
   1094 }
   1095 
   1096 UnaryMathFunctionType js::GetUnaryMathFunctionPtr(UnaryMathFunction fun) {
   1097  switch (fun) {
   1098    case UnaryMathFunction::SinNative:
   1099      return math_sin_native_impl;
   1100    case UnaryMathFunction::SinFdlibm:
   1101      return math_sin_fdlibm_impl;
   1102    case UnaryMathFunction::CosNative:
   1103      return math_cos_native_impl;
   1104    case UnaryMathFunction::CosFdlibm:
   1105      return math_cos_fdlibm_impl;
   1106    case UnaryMathFunction::TanNative:
   1107      return math_tan_native_impl;
   1108    case UnaryMathFunction::TanFdlibm:
   1109      return math_tan_fdlibm_impl;
   1110    case UnaryMathFunction::Log:
   1111      return math_log_impl;
   1112    case UnaryMathFunction::Exp:
   1113      return math_exp_impl;
   1114    case UnaryMathFunction::ATan:
   1115      return math_atan_impl;
   1116    case UnaryMathFunction::ASin:
   1117      return math_asin_impl;
   1118    case UnaryMathFunction::ACos:
   1119      return math_acos_impl;
   1120    case UnaryMathFunction::Log10:
   1121      return math_log10_impl;
   1122    case UnaryMathFunction::Log2:
   1123      return math_log2_impl;
   1124    case UnaryMathFunction::Log1P:
   1125      return math_log1p_impl;
   1126    case UnaryMathFunction::ExpM1:
   1127      return math_expm1_impl;
   1128    case UnaryMathFunction::CosH:
   1129      return math_cosh_impl;
   1130    case UnaryMathFunction::SinH:
   1131      return math_sinh_impl;
   1132    case UnaryMathFunction::TanH:
   1133      return math_tanh_impl;
   1134    case UnaryMathFunction::ACosH:
   1135      return math_acosh_impl;
   1136    case UnaryMathFunction::ASinH:
   1137      return math_asinh_impl;
   1138    case UnaryMathFunction::ATanH:
   1139      return math_atanh_impl;
   1140    case UnaryMathFunction::Trunc:
   1141      return math_trunc_impl;
   1142    case UnaryMathFunction::Cbrt:
   1143      return math_cbrt_impl;
   1144    case UnaryMathFunction::Floor:
   1145      return math_floor_impl;
   1146    case UnaryMathFunction::Ceil:
   1147      return math_ceil_impl;
   1148    case UnaryMathFunction::Round:
   1149      return math_round_impl;
   1150  }
   1151  MOZ_CRASH("Unknown function");
   1152 }
   1153 
   1154 const char* js::GetUnaryMathFunctionName(UnaryMathFunction fun, bool enumName) {
   1155  switch (fun) {
   1156    case UnaryMathFunction::SinNative:
   1157      return enumName ? "SinNative" : "Sin (native)";
   1158    case UnaryMathFunction::SinFdlibm:
   1159      return enumName ? "SinFdlibm" : "Sin (fdlibm)";
   1160    case UnaryMathFunction::CosNative:
   1161      return enumName ? "CosNative" : "Cos (native)";
   1162    case UnaryMathFunction::CosFdlibm:
   1163      return enumName ? "CosFdlibm" : "Cos (fdlibm)";
   1164    case UnaryMathFunction::TanNative:
   1165      return enumName ? "TanNative" : "Tan (native)";
   1166    case UnaryMathFunction::TanFdlibm:
   1167      return enumName ? "TanFdlibm" : "Tan (fdlibm)";
   1168    case UnaryMathFunction::Log:
   1169      return "Log";
   1170    case UnaryMathFunction::Exp:
   1171      return "Exp";
   1172    case UnaryMathFunction::ACos:
   1173      return "ACos";
   1174    case UnaryMathFunction::ASin:
   1175      return "ASin";
   1176    case UnaryMathFunction::ATan:
   1177      return "ATan";
   1178    case UnaryMathFunction::Log10:
   1179      return "Log10";
   1180    case UnaryMathFunction::Log2:
   1181      return "Log2";
   1182    case UnaryMathFunction::Log1P:
   1183      return "Log1P";
   1184    case UnaryMathFunction::ExpM1:
   1185      return "ExpM1";
   1186    case UnaryMathFunction::CosH:
   1187      return "CosH";
   1188    case UnaryMathFunction::SinH:
   1189      return "SinH";
   1190    case UnaryMathFunction::TanH:
   1191      return "TanH";
   1192    case UnaryMathFunction::ACosH:
   1193      return "ACosH";
   1194    case UnaryMathFunction::ASinH:
   1195      return "ASinH";
   1196    case UnaryMathFunction::ATanH:
   1197      return "ATanH";
   1198    case UnaryMathFunction::Trunc:
   1199      return "Trunc";
   1200    case UnaryMathFunction::Cbrt:
   1201      return "Cbrt";
   1202    case UnaryMathFunction::Floor:
   1203      return "Floor";
   1204    case UnaryMathFunction::Ceil:
   1205      return "Ceil";
   1206    case UnaryMathFunction::Round:
   1207      return "Round";
   1208  }
   1209  MOZ_CRASH("Unknown function");
   1210 }
   1211 
   1212 static const JSFunctionSpec math_static_methods[] = {
   1213    JS_FN("toSource", math_toSource, 0, 0),
   1214    JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs),
   1215    JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos),
   1216    JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin),
   1217    JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan),
   1218    JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2),
   1219    JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil),
   1220    JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32),
   1221    JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos),
   1222    JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp),
   1223    JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor),
   1224    JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul),
   1225    JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound),
   1226    JS_INLINABLE_FN("f16round", math_f16round, 1, 0, MathF16Round),
   1227    JS_INLINABLE_FN("log", math_log, 1, 0, MathLog),
   1228    JS_INLINABLE_FN("max", math_max, 2, 0, MathMax),
   1229    JS_INLINABLE_FN("min", math_min, 2, 0, MathMin),
   1230    JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow),
   1231    JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom),
   1232    JS_INLINABLE_FN("round", math_round, 1, 0, MathRound),
   1233    JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin),
   1234    JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt),
   1235    JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan),
   1236    JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10),
   1237    JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2),
   1238    JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P),
   1239    JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1),
   1240    JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH),
   1241    JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH),
   1242    JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH),
   1243    JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH),
   1244    JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
   1245    JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
   1246    JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
   1247    JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
   1248    JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
   1249    JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
   1250    JS_FN("sumPrecise", math_sumPrecise, 1, 0),
   1251    JS_FS_END,
   1252 };
   1253 
   1254 static const JSPropertySpec math_static_properties[] = {
   1255    JS_DOUBLE_PS("E", M_E, JSPROP_READONLY | JSPROP_PERMANENT),
   1256    JS_DOUBLE_PS("LOG2E", M_LOG2E, JSPROP_READONLY | JSPROP_PERMANENT),
   1257    JS_DOUBLE_PS("LOG10E", M_LOG10E, JSPROP_READONLY | JSPROP_PERMANENT),
   1258    JS_DOUBLE_PS("LN2", M_LN2, JSPROP_READONLY | JSPROP_PERMANENT),
   1259    JS_DOUBLE_PS("LN10", M_LN10, JSPROP_READONLY | JSPROP_PERMANENT),
   1260    JS_DOUBLE_PS("PI", M_PI, JSPROP_READONLY | JSPROP_PERMANENT),
   1261    JS_DOUBLE_PS("SQRT2", M_SQRT2, JSPROP_READONLY | JSPROP_PERMANENT),
   1262    JS_DOUBLE_PS("SQRT1_2", M_SQRT1_2, JSPROP_READONLY | JSPROP_PERMANENT),
   1263 
   1264    JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY),
   1265    JS_PS_END,
   1266 };
   1267 
   1268 static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
   1269  RootedObject proto(cx, &cx->global()->getObjectPrototype());
   1270  return NewTenuredObjectWithGivenProto(cx, &MathClass, proto);
   1271 }
   1272 
   1273 static const ClassSpec MathClassSpec = {
   1274    CreateMathObject,
   1275    nullptr,
   1276    math_static_methods,
   1277    math_static_properties,
   1278    nullptr,
   1279    nullptr,
   1280    nullptr,
   1281 };
   1282 
   1283 const JSClass js::MathClass = {
   1284    "Math",
   1285    JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
   1286    JS_NULL_CLASS_OPS,
   1287    &MathClassSpec,
   1288 };
   1289 
   1290 #undef ALIGN_STACK_FOR_ROUNDING_FUNCTION