tor-browser

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

jsnum.cpp (65581B)


      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 number type and wrapper class.
      9 */
     10 
     11 #include "jsnum.h"
     12 
     13 #include "mozilla/Casting.h"
     14 #include "mozilla/FloatingPoint.h"
     15 #include "mozilla/Maybe.h"
     16 #include "mozilla/RangedPtr.h"
     17 #include "mozilla/TextUtils.h"
     18 #include "mozilla/Utf8.h"
     19 
     20 #include <algorithm>
     21 #include <charconv>
     22 #include <iterator>
     23 #include <limits>
     24 #ifdef HAVE_LOCALECONV
     25 #  include <locale.h>
     26 #endif
     27 #include <math.h>
     28 #include <string.h>  // memmove
     29 #include <string_view>
     30 
     31 #include "jstypes.h"
     32 
     33 #if JS_HAS_INTL_API
     34 #  include "builtin/intl/GlobalIntlData.h"
     35 #  include "builtin/intl/NumberFormat.h"
     36 #endif
     37 #include "builtin/String.h"
     38 #include "double-conversion/double-conversion.h"
     39 #include "frontend/ParserAtom.h"  // frontend::{ParserAtomsTable, TaggedParserAtomIndex}
     40 #include "jit/InlinableNatives.h"
     41 #include "js/CharacterEncoding.h"
     42 #include "js/Conversions.h"
     43 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     44 #include "js/GCAPI.h"
     45 #if !JS_HAS_INTL_API
     46 #  include "js/LocaleSensitive.h"
     47 #endif
     48 #include "js/PropertyAndElement.h"  // JS_DefineFunctions
     49 #include "js/PropertySpec.h"
     50 #include "util/DoubleToString.h"
     51 #include "util/Memory.h"
     52 #include "util/StringBuilder.h"
     53 #include "vm/BigIntType.h"
     54 #include "vm/GlobalObject.h"
     55 #include "vm/JSAtomUtils.h"  // Atomize, AtomizeString
     56 #include "vm/JSContext.h"
     57 #include "vm/JSObject.h"
     58 #include "vm/StaticStrings.h"
     59 
     60 #include "vm/Compartment-inl.h"  // For js::UnwrapAndTypeCheckThis
     61 #include "vm/GeckoProfiler-inl.h"
     62 #include "vm/NativeObject-inl.h"
     63 #include "vm/NumberObject-inl.h"
     64 #include "vm/StringType-inl.h"
     65 
     66 using namespace js;
     67 
     68 using mozilla::Abs;
     69 using mozilla::AsciiAlphanumericToNumber;
     70 using mozilla::IsAsciiAlphanumeric;
     71 using mozilla::IsAsciiDigit;
     72 using mozilla::MaxNumberValue;
     73 using mozilla::Maybe;
     74 using mozilla::MinNumberValue;
     75 using mozilla::NegativeInfinity;
     76 using mozilla::NumberEqualsInt32;
     77 using mozilla::PositiveInfinity;
     78 using mozilla::RangedPtr;
     79 using mozilla::Utf8AsUnsignedChars;
     80 using mozilla::Utf8Unit;
     81 
     82 using JS::AutoCheckCannotGC;
     83 using JS::GenericNaN;
     84 using JS::ToInt16;
     85 using JS::ToInt32;
     86 using JS::ToInt64;
     87 using JS::ToInt8;
     88 using JS::ToUint16;
     89 using JS::ToUint32;
     90 using JS::ToUint64;
     91 using JS::ToUint8;
     92 
     93 static bool EnsureDtoaState(JSContext* cx) {
     94  if (!cx->dtoaState) {
     95    cx->dtoaState = NewDtoaState();
     96    if (!cx->dtoaState) {
     97      return false;
     98    }
     99  }
    100  return true;
    101 }
    102 
    103 template <typename CharT>
    104 static inline void AssertWellPlacedNumericSeparator(const CharT* s,
    105                                                    const CharT* start,
    106                                                    const CharT* end) {
    107  MOZ_ASSERT(start < end, "string is non-empty");
    108  MOZ_ASSERT(s > start, "number can't start with a separator");
    109  MOZ_ASSERT(s + 1 < end,
    110             "final character in a numeric literal can't be a separator");
    111  MOZ_ASSERT(*(s + 1) != '_',
    112             "separator can't be followed by another separator");
    113  MOZ_ASSERT(*(s - 1) != '_',
    114             "separator can't be preceded by another separator");
    115 }
    116 
    117 namespace {
    118 
    119 template <typename CharT>
    120 class BinaryDigitReader {
    121  const int base;     /* Base of number; must be a power of 2 */
    122  int digit;          /* Current digit value in radix given by base */
    123  int digitMask;      /* Mask to extract the next bit from digit */
    124  const CharT* cur;   /* Pointer to the remaining digits */
    125  const CharT* start; /* Pointer to the start of the string */
    126  const CharT* end;   /* Pointer to first non-digit */
    127 
    128 public:
    129  BinaryDigitReader(int base, const CharT* start, const CharT* end)
    130      : base(base),
    131        digit(0),
    132        digitMask(0),
    133        cur(start),
    134        start(start),
    135        end(end) {}
    136 
    137  /* Return the next binary digit from the number, or -1 if done. */
    138  int nextDigit() {
    139    if (digitMask == 0) {
    140      if (cur == end) {
    141        return -1;
    142      }
    143 
    144      int c = *cur++;
    145      if (c == '_') {
    146        AssertWellPlacedNumericSeparator(cur - 1, start, end);
    147        c = *cur++;
    148      }
    149 
    150      MOZ_ASSERT(IsAsciiAlphanumeric(c));
    151      digit = AsciiAlphanumericToNumber(c);
    152      digitMask = base >> 1;
    153    }
    154 
    155    int bit = (digit & digitMask) != 0;
    156    digitMask >>= 1;
    157    return bit;
    158  }
    159 };
    160 
    161 } /* anonymous namespace */
    162 
    163 /*
    164 * The fast result might also have been inaccurate for power-of-two bases. This
    165 * happens if the addition in value * 2 + digit causes a round-down to an even
    166 * least significant mantissa bit when the first dropped bit is a one.  If any
    167 * of the following digits in the number (which haven't been added in yet) are
    168 * nonzero, then the correct action would have been to round up instead of
    169 * down.  An example occurs when reading the number 0x1000000000000081, which
    170 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
    171 */
    172 template <typename CharT>
    173 static double ComputeAccurateBinaryBaseInteger(const CharT* start,
    174                                               const CharT* end, int base) {
    175  BinaryDigitReader<CharT> bdr(base, start, end);
    176 
    177  /* Skip leading zeroes. */
    178  int bit;
    179  do {
    180    bit = bdr.nextDigit();
    181  } while (bit == 0);
    182 
    183  MOZ_ASSERT(bit == 1);  // guaranteed by Get{Prefix,Decimal}Integer
    184 
    185  /* Gather the 53 significant bits (including the leading 1). */
    186  double value = 1.0;
    187  for (int j = 52; j > 0; j--) {
    188    bit = bdr.nextDigit();
    189    if (bit < 0) {
    190      return value;
    191    }
    192    value = value * 2 + bit;
    193  }
    194 
    195  /* bit2 is the 54th bit (the first dropped from the mantissa). */
    196  int bit2 = bdr.nextDigit();
    197  if (bit2 >= 0) {
    198    double factor = 2.0;
    199    int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
    200    int bit3;
    201 
    202    while ((bit3 = bdr.nextDigit()) >= 0) {
    203      sticky |= bit3;
    204      factor *= 2;
    205    }
    206    value += bit2 & (bit | sticky);
    207    value *= factor;
    208  }
    209 
    210  return value;
    211 }
    212 
    213 template <typename CharT>
    214 double js::ParseDecimalNumber(const mozilla::Range<const CharT> chars) {
    215  MOZ_ASSERT(chars.length() > 0);
    216  uint64_t dec = 0;
    217  RangedPtr<const CharT> s = chars.begin(), end = chars.end();
    218  do {
    219    CharT c = *s;
    220    MOZ_ASSERT('0' <= c && c <= '9');
    221    uint8_t digit = c - '0';
    222    uint64_t next = dec * 10 + digit;
    223    MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
    224               "next value won't be an integrally-precise double");
    225    dec = next;
    226  } while (++s < end);
    227  return static_cast<double>(dec);
    228 }
    229 
    230 template double js::ParseDecimalNumber(
    231    const mozilla::Range<const Latin1Char> chars);
    232 
    233 template double js::ParseDecimalNumber(
    234    const mozilla::Range<const char16_t> chars);
    235 
    236 template <typename CharT>
    237 static bool GetPrefixIntegerImpl(const CharT* start, const CharT* end, int base,
    238                                 IntegerSeparatorHandling separatorHandling,
    239                                 const CharT** endp, double* dp) {
    240  MOZ_ASSERT(start <= end);
    241  MOZ_ASSERT(2 <= base && base <= 36);
    242 
    243  const CharT* s = start;
    244  double d = 0.0;
    245  for (; s < end; s++) {
    246    CharT c = *s;
    247    if (!IsAsciiAlphanumeric(c)) {
    248      if (c == '_' &&
    249          separatorHandling == IntegerSeparatorHandling::SkipUnderscore) {
    250        AssertWellPlacedNumericSeparator(s, start, end);
    251        continue;
    252      }
    253      break;
    254    }
    255 
    256    uint8_t digit = AsciiAlphanumericToNumber(c);
    257    if (digit >= base) {
    258      break;
    259    }
    260 
    261    d = d * base + digit;
    262  }
    263 
    264  *endp = s;
    265  *dp = d;
    266 
    267  /* If we haven't reached the limit of integer precision, we're done. */
    268  if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
    269    return true;
    270  }
    271 
    272  /*
    273   * Otherwise compute the correct integer from the prefix of valid digits
    274   * if we're computing for base ten or a power of two.  Don't worry about
    275   * other bases; see ES2018, 18.2.5 `parseInt(string, radix)`, step 13.
    276   */
    277  if (base == 10) {
    278    return false;
    279  }
    280 
    281  if ((base & (base - 1)) == 0) {
    282    *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
    283  }
    284 
    285  return true;
    286 }
    287 
    288 template <typename CharT>
    289 bool js::GetPrefixInteger(const CharT* start, const CharT* end, int base,
    290                          IntegerSeparatorHandling separatorHandling,
    291                          const CharT** endp, double* dp) {
    292  if (GetPrefixIntegerImpl(start, end, base, separatorHandling, endp, dp)) {
    293    return true;
    294  }
    295 
    296  // Can only fail for base 10.
    297  MOZ_ASSERT(base == 10);
    298 
    299  // If we're accumulating a decimal number and the number is >= 2^53, then the
    300  // fast result from the loop in GetPrefixIntegerImpl may be inaccurate. Call
    301  // GetDecimal to get the correct answer.
    302  return GetDecimal(start, *endp, dp);
    303 }
    304 
    305 namespace js {
    306 
    307 template bool GetPrefixInteger(const char16_t* start, const char16_t* end,
    308                               int base,
    309                               IntegerSeparatorHandling separatorHandling,
    310                               const char16_t** endp, double* dp);
    311 
    312 template bool GetPrefixInteger(const Latin1Char* start, const Latin1Char* end,
    313                               int base,
    314                               IntegerSeparatorHandling separatorHandling,
    315                               const Latin1Char** endp, double* dp);
    316 
    317 }  // namespace js
    318 
    319 template <typename CharT>
    320 bool js::GetDecimalInteger(const CharT* start, const CharT* end, double* dp) {
    321  MOZ_ASSERT(start <= end);
    322 
    323  double d = 0.0;
    324  for (const CharT* s = start; s < end; s++) {
    325    CharT c = *s;
    326    if (c == '_') {
    327      AssertWellPlacedNumericSeparator(s, start, end);
    328      continue;
    329    }
    330    MOZ_ASSERT(IsAsciiDigit(c));
    331    int digit = c - '0';
    332    d = d * 10 + digit;
    333  }
    334 
    335  // If we haven't reached the limit of integer precision, we're done.
    336  if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
    337    *dp = d;
    338    return true;
    339  }
    340 
    341  // Otherwise compute the correct integer using GetDecimal.
    342  return GetDecimal(start, end, dp);
    343 }
    344 
    345 namespace js {
    346 
    347 template bool GetDecimalInteger(const char16_t* start, const char16_t* end,
    348                                double* dp);
    349 
    350 template bool GetDecimalInteger(const Latin1Char* start, const Latin1Char* end,
    351                                double* dp);
    352 
    353 template <>
    354 bool GetDecimalInteger<Utf8Unit>(const Utf8Unit* start, const Utf8Unit* end,
    355                                 double* dp) {
    356  return GetDecimalInteger(Utf8AsUnsignedChars(start), Utf8AsUnsignedChars(end),
    357                           dp);
    358 }
    359 
    360 }  // namespace js
    361 
    362 template <typename CharT>
    363 bool js::GetDecimal(const CharT* start, const CharT* end, double* dp) {
    364  MOZ_ASSERT(start <= end);
    365 
    366  size_t length = end - start;
    367 
    368  auto convert = [](auto* chars, size_t length) -> double {
    369    using SToDConverter = double_conversion::StringToDoubleConverter;
    370    SToDConverter converter(/* flags = */ 0, /* empty_string_value = */ 0.0,
    371                            /* junk_string_value = */ 0.0,
    372                            /* infinity_symbol = */ nullptr,
    373                            /* nan_symbol = */ nullptr);
    374    int lengthInt = mozilla::AssertedCast<int>(length);
    375    int processed = 0;
    376    double d = converter.StringToDouble(chars, lengthInt, &processed);
    377    MOZ_ASSERT(processed >= 0);
    378    MOZ_ASSERT(size_t(processed) == length);
    379    return d;
    380  };
    381 
    382  // If there are no underscores, we don't need to copy the chars.
    383  bool hasUnderscore = std::any_of(start, end, [](auto c) { return c == '_'; });
    384  if (!hasUnderscore) {
    385    if constexpr (std::is_same_v<CharT, char16_t>) {
    386      *dp = convert(reinterpret_cast<const uc16*>(start), length);
    387    } else {
    388      static_assert(std::is_same_v<CharT, Latin1Char>);
    389      *dp = convert(reinterpret_cast<const char*>(start), length);
    390    }
    391    return true;
    392  }
    393 
    394  Vector<char, 32, SystemAllocPolicy> chars;
    395  if (!chars.growByUninitialized(length)) {
    396    return false;
    397  }
    398 
    399  const CharT* s = start;
    400  size_t i = 0;
    401  for (; s < end; s++) {
    402    CharT c = *s;
    403    if (c == '_') {
    404      AssertWellPlacedNumericSeparator(s, start, end);
    405      continue;
    406    }
    407    MOZ_ASSERT(IsAsciiDigit(c) || c == '.' || c == 'e' || c == 'E' ||
    408               c == '+' || c == '-');
    409    chars[i++] = char(c);
    410  }
    411 
    412  *dp = convert(chars.begin(), i);
    413  return true;
    414 }
    415 
    416 namespace js {
    417 
    418 template bool GetDecimal(const char16_t* start, const char16_t* end,
    419                         double* dp);
    420 
    421 template bool GetDecimal(const Latin1Char* start, const Latin1Char* end,
    422                         double* dp);
    423 
    424 template <>
    425 bool GetDecimal<Utf8Unit>(const Utf8Unit* start, const Utf8Unit* end,
    426                          double* dp) {
    427  return GetDecimal(Utf8AsUnsignedChars(start), Utf8AsUnsignedChars(end), dp);
    428 }
    429 
    430 }  // namespace js
    431 
    432 static bool num_parseFloat(JSContext* cx, unsigned argc, Value* vp) {
    433  CallArgs args = CallArgsFromVp(argc, vp);
    434 
    435  if (args.length() == 0) {
    436    args.rval().setNaN();
    437    return true;
    438  }
    439 
    440  if (args[0].isNumber()) {
    441    // ToString(-0) is "0", handle it accordingly.
    442    if (args[0].isDouble() && args[0].toDouble() == 0.0) {
    443      args.rval().setInt32(0);
    444    } else {
    445      args.rval().set(args[0]);
    446    }
    447    return true;
    448  }
    449 
    450  JSString* str = ToString<CanGC>(cx, args[0]);
    451  if (!str) {
    452    return false;
    453  }
    454 
    455  if (str->hasIndexValue()) {
    456    args.rval().setNumber(str->getIndexValue());
    457    return true;
    458  }
    459 
    460  JSLinearString* linear = str->ensureLinear(cx);
    461  if (!linear) {
    462    return false;
    463  }
    464 
    465  double d;
    466  AutoCheckCannotGC nogc;
    467  if (linear->hasLatin1Chars()) {
    468    const Latin1Char* begin = linear->latin1Chars(nogc);
    469    const Latin1Char* end;
    470    d = js_strtod(begin, begin + linear->length(), &end);
    471    if (end == begin) {
    472      d = GenericNaN();
    473    }
    474  } else {
    475    const char16_t* begin = linear->twoByteChars(nogc);
    476    const char16_t* end;
    477    d = js_strtod(begin, begin + linear->length(), &end);
    478    if (end == begin) {
    479      d = GenericNaN();
    480    }
    481  }
    482 
    483  args.rval().setDouble(d);
    484  return true;
    485 }
    486 
    487 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
    488 // 19.2.5 parseInt ( string, radix )
    489 template <typename CharT>
    490 static bool ParseIntImpl(JSContext* cx, const CharT* chars, size_t length,
    491                         bool stripPrefix, int32_t radix, double* res) {
    492  // Step 2.
    493  const CharT* end = chars + length;
    494  const CharT* s = SkipSpace(chars, end);
    495 
    496  MOZ_ASSERT(chars <= s);
    497  MOZ_ASSERT(s <= end);
    498 
    499  // Steps 3-4.
    500  bool negative = (s != end && s[0] == '-');
    501 
    502  // Step 5. */
    503  if (s != end && (s[0] == '-' || s[0] == '+')) {
    504    s++;
    505  }
    506 
    507  // Step 10.
    508  if (stripPrefix) {
    509    if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
    510      s += 2;
    511      radix = 16;
    512    }
    513  }
    514 
    515  // Steps 11-15.
    516  const CharT* actualEnd;
    517  double d;
    518  if (!js::GetPrefixInteger(s, end, radix, IntegerSeparatorHandling::None,
    519                            &actualEnd, &d)) {
    520    ReportOutOfMemory(cx);
    521    return false;
    522  }
    523 
    524  if (s == actualEnd) {
    525    *res = GenericNaN();
    526  } else {
    527    *res = negative ? -d : d;
    528  }
    529  return true;
    530 }
    531 
    532 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
    533 // 19.2.5 parseInt ( string, radix )
    534 bool js::NumberParseInt(JSContext* cx, HandleString str, int32_t radix,
    535                        MutableHandleValue result) {
    536  // Step 7.
    537  bool stripPrefix = true;
    538 
    539  // Steps 8-9.
    540  if (radix != 0) {
    541    if (radix < 2 || radix > 36) {
    542      result.setNaN();
    543      return true;
    544    }
    545 
    546    if (radix != 16) {
    547      stripPrefix = false;
    548    }
    549  } else {
    550    radix = 10;
    551  }
    552  MOZ_ASSERT(2 <= radix && radix <= 36);
    553 
    554  JSLinearString* linear = str->ensureLinear(cx);
    555  if (!linear) {
    556    return false;
    557  }
    558 
    559  // Steps 2-5, 10-16.
    560  AutoCheckCannotGC nogc;
    561  size_t length = linear->length();
    562  double number;
    563  if (linear->hasLatin1Chars()) {
    564    if (!ParseIntImpl(cx, linear->latin1Chars(nogc), length, stripPrefix, radix,
    565                      &number)) {
    566      return false;
    567    }
    568  } else {
    569    if (!ParseIntImpl(cx, linear->twoByteChars(nogc), length, stripPrefix,
    570                      radix, &number)) {
    571      return false;
    572    }
    573  }
    574 
    575  result.setNumber(number);
    576  return true;
    577 }
    578 
    579 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
    580 // 19.2.5 parseInt ( string, radix )
    581 static bool num_parseInt(JSContext* cx, unsigned argc, Value* vp) {
    582  CallArgs args = CallArgsFromVp(argc, vp);
    583 
    584  /* Fast paths and exceptional cases. */
    585  if (args.length() == 0) {
    586    args.rval().setNaN();
    587    return true;
    588  }
    589 
    590  if (args.length() == 1 || (args[1].isInt32() && (args[1].toInt32() == 0 ||
    591                                                   args[1].toInt32() == 10))) {
    592    if (args[0].isInt32()) {
    593      args.rval().set(args[0]);
    594      return true;
    595    }
    596 
    597    /*
    598     * Step 1 is |inputString = ToString(string)|. When string >=
    599     * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
    600     * the word, which would mean the result of parseInt(string) should be |N|.
    601     *
    602     * To preserve this behaviour, we can't use the fast-path when string >=
    603     * 1e21, or else the result would be |NeM|.
    604     *
    605     * The same goes for values smaller than 1.0e-6, because the string would be
    606     * in the form of "Ne-M".
    607     */
    608    if (args[0].isDouble()) {
    609      double d = args[0].toDouble();
    610      if (DOUBLE_DECIMAL_IN_SHORTEST_LOW <= d &&
    611          d < DOUBLE_DECIMAL_IN_SHORTEST_HIGH) {
    612        args.rval().setNumber(floor(d));
    613        return true;
    614      }
    615      if (-DOUBLE_DECIMAL_IN_SHORTEST_HIGH < d &&
    616          d <= -DOUBLE_DECIMAL_IN_SHORTEST_LOW) {
    617        args.rval().setNumber(-floor(-d));
    618        return true;
    619      }
    620      if (d == 0.0) {
    621        args.rval().setInt32(0);
    622        return true;
    623      }
    624    }
    625 
    626    if (args[0].isString()) {
    627      JSString* str = args[0].toString();
    628      if (str->hasIndexValue()) {
    629        args.rval().setNumber(str->getIndexValue());
    630        return true;
    631      }
    632    }
    633  }
    634 
    635  // Step 1.
    636  RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
    637  if (!inputString) {
    638    return false;
    639  }
    640 
    641  // Step 6.
    642  int32_t radix = 0;
    643  if (args.hasDefined(1)) {
    644    if (!ToInt32(cx, args[1], &radix)) {
    645      return false;
    646    }
    647  }
    648 
    649  // Steps 2-5, 7-16.
    650  return NumberParseInt(cx, inputString, radix, args.rval());
    651 }
    652 
    653 static constexpr JSFunctionSpec number_functions[] = {
    654    JS_SELF_HOSTED_FN("isNaN", "Global_isNaN", 1, JSPROP_RESOLVING),
    655    JS_SELF_HOSTED_FN("isFinite", "Global_isFinite", 1, JSPROP_RESOLVING),
    656    JS_FS_END,
    657 };
    658 
    659 const JSClass NumberObject::class_ = {
    660    "Number",
    661    JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
    662    JS_NULL_CLASS_OPS,
    663    &NumberObject::classSpec_,
    664 };
    665 
    666 static bool Number(JSContext* cx, unsigned argc, Value* vp) {
    667  CallArgs args = CallArgsFromVp(argc, vp);
    668 
    669  if (args.length() > 0) {
    670    // BigInt proposal section 6.2, steps 2a-c.
    671    if (!ToNumeric(cx, args[0])) {
    672      return false;
    673    }
    674    if (args[0].isBigInt()) {
    675      args[0].setNumber(BigInt::numberValue(args[0].toBigInt()));
    676    }
    677    MOZ_ASSERT(args[0].isNumber());
    678  }
    679 
    680  if (!args.isConstructing()) {
    681    if (args.length() > 0) {
    682      args.rval().set(args[0]);
    683    } else {
    684      args.rval().setInt32(0);
    685    }
    686    return true;
    687  }
    688 
    689  RootedObject proto(cx);
    690  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Number, &proto)) {
    691    return false;
    692  }
    693 
    694  double d = args.length() > 0 ? args[0].toNumber() : 0;
    695  JSObject* obj = NumberObject::create(cx, d, proto);
    696  if (!obj) {
    697    return false;
    698  }
    699  args.rval().setObject(*obj);
    700  return true;
    701 }
    702 
    703 // ES2020 draft rev e08b018785606bc6465a0456a79604b149007932
    704 // 20.1.3 Properties of the Number Prototype Object, thisNumberValue.
    705 MOZ_ALWAYS_INLINE
    706 static bool ThisNumberValue(JSContext* cx, const CallArgs& args,
    707                            const char* methodName, double* number) {
    708  HandleValue thisv = args.thisv();
    709 
    710  // Step 1.
    711  if (thisv.isNumber()) {
    712    *number = thisv.toNumber();
    713    return true;
    714  }
    715 
    716  // Steps 2-3.
    717  auto* obj = UnwrapAndTypeCheckThis<NumberObject>(cx, args, methodName);
    718  if (!obj) {
    719    return false;
    720  }
    721 
    722  *number = obj->unbox();
    723  return true;
    724 }
    725 
    726 static bool num_toSource(JSContext* cx, unsigned argc, Value* vp) {
    727  CallArgs args = CallArgsFromVp(argc, vp);
    728 
    729  double d;
    730  if (!ThisNumberValue(cx, args, "toSource", &d)) {
    731    return false;
    732  }
    733 
    734  JSStringBuilder sb(cx);
    735  if (!sb.append("(new Number(") ||
    736      !NumberValueToStringBuilder(NumberValue(d), sb) || !sb.append("))")) {
    737    return false;
    738  }
    739 
    740  JSString* str = sb.finishString();
    741  if (!str) {
    742    return false;
    743  }
    744  args.rval().setString(str);
    745  return true;
    746 }
    747 
    748 // Subtract one from DTOSTR_STANDARD_BUFFER_SIZE to exclude the null-character.
    749 static_assert(
    750    double_conversion::DoubleToStringConverter::kMaxCharsEcmaScriptShortest ==
    751        DTOSTR_STANDARD_BUFFER_SIZE - 1,
    752    "double_conversion and dtoa both agree how large the longest string "
    753    "can be");
    754 
    755 static_assert(DTOSTR_STANDARD_BUFFER_SIZE <= JS::MaximumNumberToStringLength,
    756              "MaximumNumberToStringLength is large enough to hold the longest "
    757              "string produced by a conversion");
    758 
    759 MOZ_ALWAYS_INLINE
    760 static JSLinearString* LookupInt32ToString(JSContext* cx, int32_t si) {
    761  if (StaticStrings::hasInt(si)) {
    762    return cx->staticStrings().getInt(si);
    763  }
    764  return cx->realm()->dtoaCache.lookup(10, si);
    765 }
    766 
    767 template <AllowGC allowGC>
    768 JSLinearString* js::Int32ToString(JSContext* cx, int32_t si) {
    769  return js::Int32ToStringWithHeap<allowGC>(cx, si, gc::Heap::Default);
    770 }
    771 template JSLinearString* js::Int32ToString<CanGC>(JSContext* cx, int32_t si);
    772 template JSLinearString* js::Int32ToString<NoGC>(JSContext* cx, int32_t si);
    773 
    774 template <AllowGC allowGC>
    775 JSLinearString* js::Int32ToStringWithHeap(JSContext* cx, int32_t si,
    776                                          gc::Heap heap) {
    777  if (JSLinearString* str = LookupInt32ToString(cx, si)) {
    778    return str;
    779  }
    780 
    781  char buffer[JSFatInlineString::MAX_LENGTH_LATIN1];
    782 
    783  auto result = std::to_chars(buffer, std::end(buffer), si, 10);
    784  MOZ_ASSERT(result.ec == std::errc());
    785 
    786  size_t length = result.ptr - buffer;
    787  const auto& latin1Chars =
    788      reinterpret_cast<const JS::Latin1Char(&)[std::size(buffer)]>(buffer);
    789  JSInlineString* str = NewInlineString<allowGC>(cx, latin1Chars, length, heap);
    790  if (!str) {
    791    return nullptr;
    792  }
    793  if (si >= 0) {
    794    str->maybeInitializeIndexValue(si);
    795  }
    796 
    797  cx->realm()->dtoaCache.cache(10, si, str);
    798  return str;
    799 }
    800 template JSLinearString* js::Int32ToStringWithHeap<CanGC>(JSContext* cx,
    801                                                          int32_t si,
    802                                                          gc::Heap heap);
    803 template JSLinearString* js::Int32ToStringWithHeap<NoGC>(JSContext* cx,
    804                                                         int32_t si,
    805                                                         gc::Heap heap);
    806 
    807 JSLinearString* js::Int32ToStringPure(JSContext* cx, int32_t si) {
    808  AutoUnsafeCallWithABI unsafe;
    809  return Int32ToString<NoGC>(cx, si);
    810 }
    811 
    812 JSAtom* js::Int32ToAtom(JSContext* cx, int32_t si) {
    813  if (JSLinearString* str = LookupInt32ToString(cx, si)) {
    814    return js::AtomizeString(cx, str);
    815  }
    816 
    817  Int32ToCStringBuf cbuf;
    818  auto result = std::to_chars(cbuf.sbuf, std::end(cbuf.sbuf), si, 10);
    819  MOZ_ASSERT(result.ec == std::errc());
    820 
    821  Maybe<uint32_t> indexValue;
    822  if (si >= 0) {
    823    indexValue.emplace(si);
    824  }
    825 
    826  size_t length = result.ptr - cbuf.sbuf;
    827  JSAtom* atom = Atomize(cx, cbuf.sbuf, length, indexValue);
    828  if (!atom) {
    829    return nullptr;
    830  }
    831 
    832  cx->realm()->dtoaCache.cache(10, si, atom);
    833  return atom;
    834 }
    835 
    836 frontend::TaggedParserAtomIndex js::Int32ToParserAtom(
    837    FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, int32_t si) {
    838  Int32ToCStringBuf cbuf;
    839  auto result = std::to_chars(cbuf.sbuf, std::end(cbuf.sbuf), si, 10);
    840  MOZ_ASSERT(result.ec == std::errc());
    841 
    842  size_t length = result.ptr - cbuf.sbuf;
    843  return parserAtoms.internAscii(fc, cbuf.sbuf, length);
    844 }
    845 
    846 /* Returns the number of digits written. */
    847 template <typename T, size_t Base, size_t Length>
    848 static size_t Int32ToCString(char (&out)[Length], T i) {
    849  // The buffer needs to be large enough to hold the largest number, including
    850  // the sign and the terminating null-character.
    851  if constexpr (Base == 10) {
    852    static_assert(std::numeric_limits<T>::digits10 + 1 + std::is_signed_v<T> <
    853                  Length);
    854  } else {
    855    // Compute digits16 analog to std::numeric_limits::digits10, which is
    856    // defined as |std::numeric_limits::digits * std::log10(2)| for integer
    857    // types.
    858    // Note: log16(2) is 1/4.
    859    static_assert(Base == 16);
    860    static_assert(((std::numeric_limits<T>::digits + std::is_signed_v<T>) / 4 +
    861                   std::is_signed_v<T>) < Length);
    862  }
    863 
    864  // -1 to leave space for the terminating null-character.
    865  auto result = std::to_chars(out, std::end(out) - 1, i, Base);
    866  MOZ_ASSERT(result.ec == std::errc());
    867 
    868  // Null-terminate the result.
    869  *result.ptr = '\0';
    870 
    871  return result.ptr - out;
    872 }
    873 
    874 /* Returns the number of digits written. */
    875 template <typename T, size_t Base = 10>
    876 static size_t Int32ToCString(ToCStringBuf* cbuf, T i) {
    877  return Int32ToCString<T, Base>(cbuf->sbuf, i);
    878 }
    879 
    880 /* Returns the number of digits written. */
    881 template <typename T, size_t Base = 10>
    882 static size_t Int32ToCString(Int32ToCStringBuf* cbuf, T i) {
    883  return Int32ToCString<T, Base>(cbuf->sbuf, i);
    884 }
    885 
    886 template <AllowGC allowGC>
    887 static JSString* NumberToStringWithBase(JSContext* cx, double d, int32_t base);
    888 
    889 static bool num_toString(JSContext* cx, unsigned argc, Value* vp) {
    890  CallArgs args = CallArgsFromVp(argc, vp);
    891 
    892  double d;
    893  if (!ThisNumberValue(cx, args, "toString", &d)) {
    894    return false;
    895  }
    896 
    897  int32_t base = 10;
    898  if (args.hasDefined(0)) {
    899    double d2;
    900    if (!ToInteger(cx, args[0], &d2)) {
    901      return false;
    902    }
    903 
    904    if (d2 < 2 || d2 > 36) {
    905      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
    906      return false;
    907    }
    908 
    909    base = int32_t(d2);
    910  }
    911  JSString* str = NumberToStringWithBase<CanGC>(cx, d, base);
    912  if (!str) {
    913    return false;
    914  }
    915  args.rval().setString(str);
    916  return true;
    917 }
    918 
    919 /**
    920 * Number.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )
    921 *
    922 * ES2025 draft rev e42d11da7753bd933b1e7a5f3cb657ab0a8f6251
    923 *
    924 * Number.prototype.toLocaleString ( [ locales [ , options ] ] )
    925 *
    926 * ES2025 Intl draft rev 6827e6e40b45fb313472595be31352451a2d85fa
    927 */
    928 static bool num_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
    929  AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype",
    930                                        "toLocaleString");
    931  CallArgs args = CallArgsFromVp(argc, vp);
    932 
    933  // Step 1.
    934  double d;
    935  if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
    936    return false;
    937  }
    938 
    939 #if JS_HAS_INTL_API
    940  HandleValue locales = args.get(0);
    941  HandleValue options = args.get(1);
    942 
    943  // Step 2.
    944  Rooted<NumberFormatObject*> numberFormat(
    945      cx, intl::GetOrCreateNumberFormat(cx, locales, options));
    946  if (!numberFormat) {
    947    return false;
    948  }
    949 
    950  // Step 3.
    951  JSString* str = intl::FormatNumber(cx, numberFormat, d);
    952  if (!str) {
    953    return false;
    954  }
    955  args.rval().setString(str);
    956  return true;
    957 #else
    958  RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
    959  if (!str) {
    960    return false;
    961  }
    962 
    963  /*
    964   * Create the string, move back to bytes to make string twiddling
    965   * a bit easier and so we can insert platform charset seperators.
    966   */
    967  UniqueChars numBytes = EncodeAscii(cx, str);
    968  if (!numBytes) {
    969    return false;
    970  }
    971  const char* num = numBytes.get();
    972  if (!num) {
    973    return false;
    974  }
    975 
    976  /*
    977   * Find the first non-integer value, whether it be a letter as in
    978   * 'Infinity', a decimal point, or an 'e' from exponential notation.
    979   */
    980  const char* nint = num;
    981  if (*nint == '-') {
    982    nint++;
    983  }
    984  while (*nint >= '0' && *nint <= '9') {
    985    nint++;
    986  }
    987  int digits = nint - num;
    988  const char* end = num + digits;
    989  if (!digits) {
    990    args.rval().setString(str);
    991    return true;
    992  }
    993 
    994  JSRuntime* rt = cx->runtime();
    995  size_t thousandsLength = strlen(rt->thousandsSeparator);
    996  size_t decimalLength = strlen(rt->decimalSeparator);
    997 
    998  /* Figure out how long resulting string will be. */
    999  int buflen = strlen(num);
   1000  if (*nint == '.') {
   1001    buflen += decimalLength - 1; /* -1 to account for existing '.' */
   1002  }
   1003 
   1004  const char* numGrouping;
   1005  const char* tmpGroup;
   1006  numGrouping = tmpGroup = rt->numGrouping;
   1007  int remainder = digits;
   1008  if (*num == '-') {
   1009    remainder--;
   1010  }
   1011 
   1012  while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
   1013    if (*tmpGroup >= remainder) {
   1014      break;
   1015    }
   1016    buflen += thousandsLength;
   1017    remainder -= *tmpGroup;
   1018    tmpGroup++;
   1019  }
   1020 
   1021  int nrepeat;
   1022  if (*tmpGroup == '\0' && *numGrouping != '\0') {
   1023    nrepeat = (remainder - 1) / tmpGroup[-1];
   1024    buflen += thousandsLength * nrepeat;
   1025    remainder -= nrepeat * tmpGroup[-1];
   1026  } else {
   1027    nrepeat = 0;
   1028  }
   1029  tmpGroup--;
   1030 
   1031  char* buf = cx->pod_malloc<char>(buflen + 1);
   1032  if (!buf) {
   1033    return false;
   1034  }
   1035 
   1036  char* tmpDest = buf;
   1037  const char* tmpSrc = num;
   1038 
   1039  while (*tmpSrc == '-' || remainder--) {
   1040    MOZ_ASSERT(tmpDest - buf < buflen);
   1041    *tmpDest++ = *tmpSrc++;
   1042  }
   1043  while (tmpSrc < end) {
   1044    MOZ_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
   1045    strcpy(tmpDest, rt->thousandsSeparator);
   1046    tmpDest += thousandsLength;
   1047    MOZ_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
   1048    js_memcpy(tmpDest, tmpSrc, *tmpGroup);
   1049    tmpDest += *tmpGroup;
   1050    tmpSrc += *tmpGroup;
   1051    if (--nrepeat < 0) {
   1052      tmpGroup--;
   1053    }
   1054  }
   1055 
   1056  if (*nint == '.') {
   1057    MOZ_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
   1058    strcpy(tmpDest, rt->decimalSeparator);
   1059    tmpDest += decimalLength;
   1060    MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
   1061    strcpy(tmpDest, nint + 1);
   1062  } else {
   1063    MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
   1064    strcpy(tmpDest, nint);
   1065  }
   1066 
   1067  if (cx->runtime()->localeCallbacks &&
   1068      cx->runtime()->localeCallbacks->localeToUnicode) {
   1069    Rooted<Value> v(cx, StringValue(str));
   1070    bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v);
   1071    if (ok) {
   1072      args.rval().set(v);
   1073    }
   1074    js_free(buf);
   1075    return ok;
   1076  }
   1077 
   1078  str = NewStringCopyN<CanGC>(cx, buf, buflen);
   1079  js_free(buf);
   1080  if (!str) {
   1081    return false;
   1082  }
   1083 
   1084  args.rval().setString(str);
   1085  return true;
   1086 #endif
   1087 }
   1088 
   1089 bool js::num_valueOf(JSContext* cx, unsigned argc, Value* vp) {
   1090  CallArgs args = CallArgsFromVp(argc, vp);
   1091 
   1092  double d;
   1093  if (!ThisNumberValue(cx, args, "valueOf", &d)) {
   1094    return false;
   1095  }
   1096 
   1097  args.rval().setNumber(d);
   1098  return true;
   1099 }
   1100 
   1101 static const unsigned MAX_PRECISION = 100;
   1102 
   1103 static bool ComputePrecisionInRange(JSContext* cx, int minPrecision,
   1104                                    int maxPrecision, double prec,
   1105                                    int* precision) {
   1106  if (minPrecision <= prec && prec <= maxPrecision) {
   1107    *precision = int(prec);
   1108    return true;
   1109  }
   1110 
   1111  ToCStringBuf cbuf;
   1112  char* numStr = NumberToCString(&cbuf, prec);
   1113  MOZ_ASSERT(numStr);
   1114  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE,
   1115                            numStr);
   1116  return false;
   1117 }
   1118 
   1119 static constexpr size_t DoubleToStrResultBufSize = 128;
   1120 
   1121 template <typename Op>
   1122 [[nodiscard]] static bool DoubleToStrResult(JSContext* cx, const CallArgs& args,
   1123                                            Op op) {
   1124  char buf[DoubleToStrResultBufSize];
   1125 
   1126  const auto& converter =
   1127      double_conversion::DoubleToStringConverter::EcmaScriptConverter();
   1128  double_conversion::StringBuilder builder(buf, sizeof(buf));
   1129 
   1130  bool ok = op(converter, builder);
   1131  MOZ_RELEASE_ASSERT(ok);
   1132 
   1133  size_t numStrLen = builder.position();
   1134  const char* numStr = builder.Finalize();
   1135  MOZ_ASSERT(numStr == buf);
   1136  MOZ_ASSERT(numStrLen == strlen(numStr));
   1137 
   1138  JSString* str = NewStringCopyN<CanGC>(cx, numStr, numStrLen);
   1139  if (!str) {
   1140    return false;
   1141  }
   1142 
   1143  args.rval().setString(str);
   1144  return true;
   1145 }
   1146 
   1147 // ES 2021 draft 21.1.3.3.
   1148 static bool num_toFixed(JSContext* cx, unsigned argc, Value* vp) {
   1149  AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype", "toFixed");
   1150  CallArgs args = CallArgsFromVp(argc, vp);
   1151 
   1152  // Step 1.
   1153  double d;
   1154  if (!ThisNumberValue(cx, args, "toFixed", &d)) {
   1155    return false;
   1156  }
   1157 
   1158  // Steps 2-5.
   1159  int precision;
   1160  if (args.length() == 0) {
   1161    precision = 0;
   1162  } else {
   1163    double prec = 0;
   1164    if (!ToInteger(cx, args[0], &prec)) {
   1165      return false;
   1166    }
   1167 
   1168    if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
   1169      return false;
   1170    }
   1171  }
   1172 
   1173  // Step 6.
   1174  if (std::isnan(d)) {
   1175    args.rval().setString(cx->names().NaN);
   1176    return true;
   1177  }
   1178  if (std::isinf(d)) {
   1179    if (d > 0) {
   1180      args.rval().setString(cx->names().Infinity);
   1181      return true;
   1182    }
   1183 
   1184    args.rval().setString(cx->names().NegativeInfinity_);
   1185    return true;
   1186  }
   1187 
   1188  // Steps 7-10 for very large numbers.
   1189  if (d <= -1e21 || d >= 1e+21) {
   1190    JSString* s = NumberToString<CanGC>(cx, d);
   1191    if (!s) {
   1192      return false;
   1193    }
   1194 
   1195    args.rval().setString(s);
   1196    return true;
   1197  }
   1198 
   1199  // Steps 7-12.
   1200 
   1201  // DoubleToStringConverter::ToFixed is documented as requiring a buffer size
   1202  // of:
   1203  //
   1204  //   1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint + 1
   1205  //   (one additional character for the sign, one for the decimal point,
   1206  //      and one for the null terminator)
   1207  //
   1208  // We already ensured there are at most 21 digits before the point, and
   1209  // MAX_PRECISION digits after the point.
   1210  static_assert(1 + 21 + 1 + MAX_PRECISION + 1 <= DoubleToStrResultBufSize);
   1211 
   1212  // The double-conversion library by default has a kMaxFixedDigitsAfterPoint of
   1213  // 60. Assert our modified version supports at least MAX_PRECISION (100).
   1214  using DToSConverter = double_conversion::DoubleToStringConverter;
   1215  static_assert(DToSConverter::kMaxFixedDigitsAfterPoint >= MAX_PRECISION);
   1216 
   1217  return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
   1218    return converter.ToFixed(d, precision, &builder);
   1219  });
   1220 }
   1221 
   1222 // ES 2021 draft 21.1.3.2.
   1223 static bool num_toExponential(JSContext* cx, unsigned argc, Value* vp) {
   1224  AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype",
   1225                                        "toExponential");
   1226  CallArgs args = CallArgsFromVp(argc, vp);
   1227 
   1228  // Step 1.
   1229  double d;
   1230  if (!ThisNumberValue(cx, args, "toExponential", &d)) {
   1231    return false;
   1232  }
   1233 
   1234  // Step 2.
   1235  double prec = 0;
   1236  if (args.hasDefined(0)) {
   1237    if (!ToInteger(cx, args[0], &prec)) {
   1238      return false;
   1239    }
   1240  }
   1241 
   1242  // Step 3.
   1243  MOZ_ASSERT_IF(!args.hasDefined(0), prec == 0);
   1244 
   1245  // Step 4.
   1246  if (std::isnan(d)) {
   1247    args.rval().setString(cx->names().NaN);
   1248    return true;
   1249  }
   1250  if (std::isinf(d)) {
   1251    if (d > 0) {
   1252      args.rval().setString(cx->names().Infinity);
   1253      return true;
   1254    }
   1255 
   1256    args.rval().setString(cx->names().NegativeInfinity_);
   1257    return true;
   1258  }
   1259 
   1260  // Step 5.
   1261  int precision = 0;
   1262  if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
   1263    return false;
   1264  }
   1265 
   1266  // Steps 6-15.
   1267 
   1268  // DoubleToStringConverter::ToExponential is documented as adding at most 8
   1269  // characters on top of the requested digits: "the sign, the digit before the
   1270  // decimal point, the decimal point, the exponent character, the exponent's
   1271  // sign, and at most 3 exponent digits". In addition, the buffer must be able
   1272  // to hold the trailing '\0' character.
   1273  static_assert(MAX_PRECISION + 8 + 1 <= DoubleToStrResultBufSize);
   1274 
   1275  return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
   1276    int requestedDigits = args.hasDefined(0) ? precision : -1;
   1277    return converter.ToExponential(d, requestedDigits, &builder);
   1278  });
   1279 }
   1280 
   1281 // ES 2021 draft 21.1.3.5.
   1282 static bool num_toPrecision(JSContext* cx, unsigned argc, Value* vp) {
   1283  AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype", "toPrecision");
   1284  CallArgs args = CallArgsFromVp(argc, vp);
   1285 
   1286  // Step 1.
   1287  double d;
   1288  if (!ThisNumberValue(cx, args, "toPrecision", &d)) {
   1289    return false;
   1290  }
   1291 
   1292  // Step 2.
   1293  if (!args.hasDefined(0)) {
   1294    JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10);
   1295    if (!str) {
   1296      return false;
   1297    }
   1298    args.rval().setString(str);
   1299    return true;
   1300  }
   1301 
   1302  // Step 3.
   1303  double prec = 0;
   1304  if (!ToInteger(cx, args[0], &prec)) {
   1305    return false;
   1306  }
   1307 
   1308  // Step 4.
   1309  if (std::isnan(d)) {
   1310    args.rval().setString(cx->names().NaN);
   1311    return true;
   1312  }
   1313  if (std::isinf(d)) {
   1314    if (d > 0) {
   1315      args.rval().setString(cx->names().Infinity);
   1316      return true;
   1317    }
   1318 
   1319    args.rval().setString(cx->names().NegativeInfinity_);
   1320    return true;
   1321  }
   1322 
   1323  // Step 5.
   1324  int precision = 0;
   1325  if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, prec, &precision)) {
   1326    return false;
   1327  }
   1328 
   1329  // Steps 6-14.
   1330 
   1331  // DoubleToStringConverter::ToPrecision is documented as adding at most 7
   1332  // characters on top of the requested digits: "the sign, the decimal point,
   1333  // the exponent character, the exponent's sign, and at most 3 exponent
   1334  // digits". In addition, the buffer must be able to hold the trailing '\0'
   1335  // character.
   1336  static_assert(MAX_PRECISION + 7 + 1 <= DoubleToStrResultBufSize);
   1337 
   1338  return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
   1339    return converter.ToPrecision(d, precision, &builder);
   1340  });
   1341 }
   1342 
   1343 static constexpr JSFunctionSpec number_methods[] = {
   1344    JS_FN("toSource", num_toSource, 0, 0),
   1345    JS_INLINABLE_FN("toString", num_toString, 1, 0, NumberToString),
   1346    JS_FN("toLocaleString", num_toLocaleString, 0, 0),
   1347    JS_FN("valueOf", num_valueOf, 0, 0),
   1348    JS_FN("toFixed", num_toFixed, 1, 0),
   1349    JS_FN("toExponential", num_toExponential, 1, 0),
   1350    JS_FN("toPrecision", num_toPrecision, 1, 0),
   1351    JS_FS_END,
   1352 };
   1353 
   1354 bool js::IsInteger(double d) {
   1355  return std::isfinite(d) && JS::ToInteger(d) == d;
   1356 }
   1357 
   1358 static constexpr JSFunctionSpec number_static_methods[] = {
   1359    JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1, 0),
   1360    JS_SELF_HOSTED_FN("isInteger", "Number_isInteger", 1, 0),
   1361    JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1, 0),
   1362    JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1, 0),
   1363    JS_FS_END,
   1364 };
   1365 
   1366 static constexpr JSPropertySpec number_static_properties[] = {
   1367    JS_DOUBLE_PS("POSITIVE_INFINITY", mozilla::PositiveInfinity<double>(),
   1368                 JSPROP_READONLY | JSPROP_PERMANENT),
   1369    JS_DOUBLE_PS("NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>(),
   1370                 JSPROP_READONLY | JSPROP_PERMANENT),
   1371    JS_DOUBLE_PS("MAX_VALUE", MaxNumberValue<double>(),
   1372                 JSPROP_READONLY | JSPROP_PERMANENT),
   1373    JS_DOUBLE_PS("MIN_VALUE", MinNumberValue<double>(),
   1374                 JSPROP_READONLY | JSPROP_PERMANENT),
   1375    /* ES6 (April 2014 draft) 20.1.2.6 */
   1376    JS_DOUBLE_PS("MAX_SAFE_INTEGER", 9007199254740991,
   1377                 JSPROP_READONLY | JSPROP_PERMANENT),
   1378    /* ES6 (April 2014 draft) 20.1.2.10 */
   1379    JS_DOUBLE_PS("MIN_SAFE_INTEGER", -9007199254740991,
   1380                 JSPROP_READONLY | JSPROP_PERMANENT),
   1381    /* ES6 (May 2013 draft) 15.7.3.7 */
   1382    JS_DOUBLE_PS("EPSILON", 2.2204460492503130808472633361816e-16,
   1383                 JSPROP_READONLY | JSPROP_PERMANENT),
   1384    JS_PS_END,
   1385 };
   1386 
   1387 bool js::InitRuntimeNumberState(JSRuntime* rt) {
   1388  // XXX If JS_HAS_INTL_API becomes true all the time at some point,
   1389  //     js::InitRuntimeNumberState is no longer fallible, and we should
   1390  //     change its return type.
   1391 #if !JS_HAS_INTL_API
   1392  /* Copy locale-specific separators into the runtime strings. */
   1393  const char* thousandsSeparator;
   1394  const char* decimalPoint;
   1395  const char* grouping;
   1396 #  ifdef HAVE_LOCALECONV
   1397  struct lconv* locale = localeconv();
   1398  thousandsSeparator = locale->thousands_sep;
   1399  decimalPoint = locale->decimal_point;
   1400  grouping = locale->grouping;
   1401 #  else
   1402  thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
   1403  decimalPoint = getenv("LOCALE_DECIMAL_POINT");
   1404  grouping = getenv("LOCALE_GROUPING");
   1405 #  endif
   1406  if (!thousandsSeparator) {
   1407    thousandsSeparator = "'";
   1408  }
   1409  if (!decimalPoint) {
   1410    decimalPoint = ".";
   1411  }
   1412  if (!grouping) {
   1413    grouping = "\3\0";
   1414  }
   1415 
   1416  /*
   1417   * We use single malloc to get the memory for all separator and grouping
   1418   * strings.
   1419   */
   1420  size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
   1421  size_t decimalPointSize = strlen(decimalPoint) + 1;
   1422  size_t groupingSize = strlen(grouping) + 1;
   1423 
   1424  char* storage = js_pod_malloc<char>(thousandsSeparatorSize +
   1425                                      decimalPointSize + groupingSize);
   1426  if (!storage) {
   1427    return false;
   1428  }
   1429 
   1430  js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
   1431  rt->thousandsSeparator = storage;
   1432  storage += thousandsSeparatorSize;
   1433 
   1434  js_memcpy(storage, decimalPoint, decimalPointSize);
   1435  rt->decimalSeparator = storage;
   1436  storage += decimalPointSize;
   1437 
   1438  js_memcpy(storage, grouping, groupingSize);
   1439  rt->numGrouping = grouping;
   1440 #endif /* !JS_HAS_INTL_API */
   1441  return true;
   1442 }
   1443 
   1444 void js::FinishRuntimeNumberState(JSRuntime* rt) {
   1445 #if !JS_HAS_INTL_API
   1446  /*
   1447   * The free also releases the memory for decimalSeparator and numGrouping
   1448   * strings.
   1449   */
   1450  char* storage = const_cast<char*>(rt->thousandsSeparator.ref());
   1451  js_free(storage);
   1452 #endif  // !JS_HAS_INTL_API
   1453 }
   1454 
   1455 JSObject* NumberObject::createPrototype(JSContext* cx, JSProtoKey key) {
   1456  NumberObject* numberProto =
   1457      GlobalObject::createBlankPrototype<NumberObject>(cx, cx->global());
   1458  if (!numberProto) {
   1459    return nullptr;
   1460  }
   1461  numberProto->setPrimitiveValue(0);
   1462  return numberProto;
   1463 }
   1464 
   1465 static bool NumberClassFinish(JSContext* cx, HandleObject ctor,
   1466                              HandleObject proto) {
   1467  Handle<GlobalObject*> global = cx->global();
   1468 
   1469  if (!JS_DefineFunctions(cx, global, number_functions)) {
   1470    return false;
   1471  }
   1472 
   1473  // Number.parseInt should be the same function object as global parseInt.
   1474  RootedId parseIntId(cx, NameToId(cx->names().parseInt));
   1475  JSFunction* parseInt =
   1476      DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING);
   1477  if (!parseInt) {
   1478    return false;
   1479  }
   1480  parseInt->setJitInfo(&jit::JitInfo_NumberParseInt);
   1481 
   1482  RootedValue parseIntValue(cx, ObjectValue(*parseInt));
   1483  if (!DefineDataProperty(cx, ctor, parseIntId, parseIntValue, 0)) {
   1484    return false;
   1485  }
   1486 
   1487  // Number.parseFloat should be the same function object as global
   1488  // parseFloat.
   1489  RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
   1490  JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
   1491                                          num_parseFloat, 1, JSPROP_RESOLVING);
   1492  if (!parseFloat) {
   1493    return false;
   1494  }
   1495  RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
   1496  if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) {
   1497    return false;
   1498  }
   1499 
   1500  RootedValue valueNaN(cx, JS::NaNValue());
   1501  RootedValue valueInfinity(cx, JS::InfinityValue());
   1502 
   1503  if (!DefineDataProperty(
   1504          cx, ctor, cx->names().NaN, valueNaN,
   1505          JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
   1506    return false;
   1507  }
   1508 
   1509  // ES5 15.1.1.1, 15.1.1.2
   1510  if (!NativeDefineDataProperty(
   1511          cx, global, cx->names().NaN, valueNaN,
   1512          JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) ||
   1513      !NativeDefineDataProperty(
   1514          cx, global, cx->names().Infinity, valueInfinity,
   1515          JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
   1516    return false;
   1517  }
   1518 
   1519  return true;
   1520 }
   1521 
   1522 const ClassSpec NumberObject::classSpec_ = {
   1523    GenericCreateConstructor<Number, 1, gc::AllocKind::FUNCTION,
   1524                             &jit::JitInfo_Number>,
   1525    NumberObject::createPrototype,
   1526    number_static_methods,
   1527    number_static_properties,
   1528    number_methods,
   1529    nullptr,
   1530    NumberClassFinish,
   1531 };
   1532 
   1533 static size_t FracNumberToCString(ToCStringBuf* cbuf, double d) {
   1534 #ifdef DEBUG
   1535  {
   1536    int32_t _;
   1537    MOZ_ASSERT(!NumberEqualsInt32(d, &_));
   1538  }
   1539 #endif
   1540 
   1541  /*
   1542   * This is V8's implementation of the algorithm described in the
   1543   * following paper:
   1544   *
   1545   *   Printing floating-point numbers quickly and accurately with integers.
   1546   *   Florian Loitsch, PLDI 2010.
   1547   */
   1548  const double_conversion::DoubleToStringConverter& converter =
   1549      double_conversion::DoubleToStringConverter::EcmaScriptConverter();
   1550  double_conversion::StringBuilder builder(cbuf->sbuf, std::size(cbuf->sbuf));
   1551  MOZ_ALWAYS_TRUE(converter.ToShortest(d, &builder));
   1552 
   1553  size_t len = builder.position();
   1554 #ifdef DEBUG
   1555  char* result =
   1556 #endif
   1557      builder.Finalize();
   1558  MOZ_ASSERT(cbuf->sbuf == result);
   1559  return len;
   1560 }
   1561 
   1562 void JS::NumberToString(double d, char (&out)[MaximumNumberToStringLength]) {
   1563  int32_t i;
   1564  if (NumberEqualsInt32(d, &i)) {
   1565    Int32ToCStringBuf cbuf;
   1566    size_t len = ::Int32ToCString(&cbuf, i);
   1567    memmove(out, cbuf.sbuf, len);
   1568    out[len] = '\0';
   1569  } else {
   1570    const double_conversion::DoubleToStringConverter& converter =
   1571        double_conversion::DoubleToStringConverter::EcmaScriptConverter();
   1572 
   1573    double_conversion::StringBuilder builder(out, sizeof(out));
   1574    MOZ_ALWAYS_TRUE(converter.ToShortest(d, &builder));
   1575 
   1576 #ifdef DEBUG
   1577    char* result =
   1578 #endif
   1579        builder.Finalize();
   1580    MOZ_ASSERT(out == result);
   1581  }
   1582 }
   1583 
   1584 char* js::NumberToCString(ToCStringBuf* cbuf, double d, size_t* length) {
   1585  int32_t i;
   1586  size_t len = NumberEqualsInt32(d, &i) ? ::Int32ToCString(cbuf, i)
   1587                                        : FracNumberToCString(cbuf, d);
   1588  if (length) {
   1589    *length = len;
   1590  }
   1591  return cbuf->sbuf;
   1592 }
   1593 
   1594 char* js::Int32ToCString(Int32ToCStringBuf* cbuf, int32_t value,
   1595                         size_t* length) {
   1596  size_t len = ::Int32ToCString(cbuf, value);
   1597  if (length) {
   1598    *length = len;
   1599  }
   1600  return cbuf->sbuf;
   1601 }
   1602 
   1603 char* js::Uint32ToCString(Int32ToCStringBuf* cbuf, uint32_t value,
   1604                          size_t* length) {
   1605  size_t len = ::Int32ToCString(cbuf, value);
   1606  if (length) {
   1607    *length = len;
   1608  }
   1609  return cbuf->sbuf;
   1610 }
   1611 
   1612 char* js::Uint32ToHexCString(Int32ToCStringBuf* cbuf, uint32_t value,
   1613                             size_t* length) {
   1614  size_t len = ::Int32ToCString<uint32_t, 16>(cbuf, value);
   1615  if (length) {
   1616    *length = len;
   1617  }
   1618  return cbuf->sbuf;
   1619 }
   1620 
   1621 template <AllowGC allowGC>
   1622 static JSLinearString* Int32ToStringWithBase(JSContext* cx, int32_t i,
   1623                                             int32_t base) {
   1624  MOZ_ASSERT(2 <= base && base <= 36);
   1625 
   1626  bool isBase10Int = (base == 10);
   1627  if (isBase10Int) {
   1628    static_assert(StaticStrings::INT_STATIC_LIMIT > 10 * 10);
   1629    if (StaticStrings::hasInt(i)) {
   1630      return cx->staticStrings().getInt(i);
   1631    }
   1632  } else if (unsigned(i) < unsigned(base)) {
   1633    if (i < 10) {
   1634      return cx->staticStrings().getInt(i);
   1635    }
   1636    char16_t c = 'a' + i - 10;
   1637    MOZ_ASSERT(StaticStrings::hasUnit(c));
   1638    return cx->staticStrings().getUnit(c);
   1639  } else if (unsigned(i) < unsigned(base * base)) {
   1640    static constexpr char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
   1641    char chars[] = {digits[i / base], digits[i % base]};
   1642    JSLinearString* str = cx->staticStrings().lookup(chars, 2);
   1643    MOZ_ASSERT(str);
   1644    return str;
   1645  }
   1646 
   1647  auto& dtoaCache = cx->realm()->dtoaCache;
   1648  double d = i;
   1649  if (JSLinearString* str = dtoaCache.lookup(base, d)) {
   1650    return str;
   1651  }
   1652 
   1653  // Plus two to include the largest number and the sign.
   1654  constexpr size_t MaximumLength = std::numeric_limits<int32_t>::digits + 2;
   1655 
   1656  char buf[MaximumLength] = {};
   1657 
   1658  // Use explicit cases for base 10 and base 16 to make it more likely the
   1659  // compiler will generate optimized code for these two common bases.
   1660  std::to_chars_result result;
   1661  switch (base) {
   1662    case 10: {
   1663      result = std::to_chars(buf, std::end(buf), i, 10);
   1664      break;
   1665    }
   1666    case 16: {
   1667      result = std::to_chars(buf, std::end(buf), i, 16);
   1668      break;
   1669    }
   1670    default: {
   1671      MOZ_ASSERT(base >= 2 && base <= 36);
   1672      result = std::to_chars(buf, std::end(buf), i, base);
   1673      break;
   1674    }
   1675  }
   1676  MOZ_ASSERT(result.ec == std::errc());
   1677 
   1678  size_t length = result.ptr - buf;
   1679  MOZ_ASSERT(i < 0 || length > 2, "small static strings are handled above");
   1680 
   1681  auto* latin1Chars = reinterpret_cast<JS::Latin1Char*>(buf);
   1682  JSLinearString* s = NewStringCopyNDontDeflateNonStaticValidLength<allowGC>(
   1683      cx, latin1Chars, length);
   1684  if (!s) {
   1685    return nullptr;
   1686  }
   1687 
   1688  if (isBase10Int && i >= 0) {
   1689    s->maybeInitializeIndexValue(i);
   1690  }
   1691 
   1692  dtoaCache.cache(base, d, s);
   1693  return s;
   1694 }
   1695 
   1696 template <AllowGC allowGC>
   1697 static JSString* NumberToStringWithBase(JSContext* cx, double d, int32_t base) {
   1698  MOZ_ASSERT(2 <= base && base <= 36);
   1699 
   1700  int32_t i;
   1701  if (NumberEqualsInt32(d, &i)) {
   1702    return ::Int32ToStringWithBase<allowGC>(cx, i, base);
   1703  }
   1704 
   1705  auto& dtoaCache = cx->realm()->dtoaCache;
   1706  if (JSLinearString* str = dtoaCache.lookup(base, d)) {
   1707    return str;
   1708  }
   1709 
   1710  JSLinearString* s;
   1711  if (base == 10) {
   1712    // We use a faster algorithm for base 10.
   1713    ToCStringBuf cbuf;
   1714    size_t numStrLen = FracNumberToCString(&cbuf, d);
   1715    MOZ_ASSERT(numStrLen == strlen(cbuf.sbuf));
   1716 
   1717    s = NewStringCopyN<allowGC>(cx, cbuf.sbuf, numStrLen);
   1718    if (!s) {
   1719      return nullptr;
   1720    }
   1721  } else {
   1722    if (!EnsureDtoaState(cx)) {
   1723      if constexpr (allowGC) {
   1724        ReportOutOfMemory(cx);
   1725      }
   1726      return nullptr;
   1727    }
   1728 
   1729    UniqueChars numStr(js_dtobasestr(cx->dtoaState, base, d));
   1730    if (!numStr) {
   1731      if constexpr (allowGC) {
   1732        ReportOutOfMemory(cx);
   1733      }
   1734      return nullptr;
   1735    }
   1736 
   1737    s = NewStringCopyZ<allowGC>(cx, numStr.get());
   1738    if (!s) {
   1739      return nullptr;
   1740    }
   1741  }
   1742 
   1743  dtoaCache.cache(base, d, s);
   1744  return s;
   1745 }
   1746 
   1747 template <AllowGC allowGC>
   1748 JSString* js::NumberToString(JSContext* cx, double d) {
   1749  return NumberToStringWithBase<allowGC>(cx, d, 10);
   1750 }
   1751 
   1752 template JSString* js::NumberToString<CanGC>(JSContext* cx, double d);
   1753 
   1754 template JSString* js::NumberToString<NoGC>(JSContext* cx, double d);
   1755 
   1756 JSString* js::NumberToStringPure(JSContext* cx, double d) {
   1757  AutoUnsafeCallWithABI unsafe;
   1758  return NumberToString<NoGC>(cx, d);
   1759 }
   1760 
   1761 JSAtom* js::NumberToAtom(JSContext* cx, double d) {
   1762  int32_t si;
   1763  if (NumberEqualsInt32(d, &si)) {
   1764    return Int32ToAtom(cx, si);
   1765  }
   1766 
   1767  auto& dtoaCache = cx->realm()->dtoaCache;
   1768  if (JSLinearString* str = dtoaCache.lookup(10, d)) {
   1769    return AtomizeString(cx, str);
   1770  }
   1771 
   1772  ToCStringBuf cbuf;
   1773  size_t length = FracNumberToCString(&cbuf, d);
   1774  MOZ_ASSERT(length == strlen(cbuf.sbuf));
   1775 
   1776  JSAtom* atom = Atomize(cx, cbuf.sbuf, length);
   1777  if (!atom) {
   1778    return nullptr;
   1779  }
   1780 
   1781  dtoaCache.cache(10, d, atom);
   1782  return atom;
   1783 }
   1784 
   1785 frontend::TaggedParserAtomIndex js::NumberToParserAtom(
   1786    FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, double d) {
   1787  int32_t si;
   1788  if (NumberEqualsInt32(d, &si)) {
   1789    return Int32ToParserAtom(fc, parserAtoms, si);
   1790  }
   1791 
   1792  ToCStringBuf cbuf;
   1793  size_t length = FracNumberToCString(&cbuf, d);
   1794  MOZ_ASSERT(length == strlen(cbuf.sbuf));
   1795 
   1796  return parserAtoms.internAscii(fc, cbuf.sbuf, length);
   1797 }
   1798 
   1799 JSLinearString* js::IndexToString(JSContext* cx, uint32_t index) {
   1800  if (StaticStrings::hasUint(index)) {
   1801    return cx->staticStrings().getUint(index);
   1802  }
   1803 
   1804  char buffer[JSFatInlineString::MAX_LENGTH_LATIN1];
   1805 
   1806  auto result = std::to_chars(buffer, std::end(buffer), index, 10);
   1807  MOZ_ASSERT(result.ec == std::errc());
   1808 
   1809  size_t length = result.ptr - buffer;
   1810  const auto& latin1Chars =
   1811      reinterpret_cast<const JS::Latin1Char(&)[std::size(buffer)]>(buffer);
   1812  return NewInlineString<CanGC>(cx, latin1Chars, length);
   1813 }
   1814 
   1815 template <AllowGC allowGC>
   1816 JSLinearString* js::Int32ToStringWithBase(JSContext* cx, int32_t i,
   1817                                          int32_t base, bool lowerCase) {
   1818  JSLinearString* str = ::Int32ToStringWithBase<allowGC>(cx, i, base);
   1819  if (!str) {
   1820    return nullptr;
   1821  }
   1822 
   1823  if constexpr (allowGC == NoGC) {
   1824    MOZ_ASSERT(lowerCase, "upper case conversion not allowed for NoGC");
   1825    return str;
   1826  } else {
   1827    if (lowerCase) {
   1828      return str;
   1829    }
   1830    return StringToUpperCase(cx, str);
   1831  }
   1832 }
   1833 template JSLinearString* js::Int32ToStringWithBase<CanGC>(JSContext* cx,
   1834                                                          int32_t i,
   1835                                                          int32_t base,
   1836                                                          bool lowerCase);
   1837 template JSLinearString* js::Int32ToStringWithBase<NoGC>(JSContext* cx,
   1838                                                         int32_t i,
   1839                                                         int32_t base,
   1840                                                         bool lowerCase);
   1841 
   1842 bool js::NumberValueToStringBuilder(const Value& v, StringBuilder& sb) {
   1843  /* Convert to C-string. */
   1844  ToCStringBuf cbuf;
   1845  const char* cstr;
   1846  size_t cstrlen;
   1847  if (v.isInt32()) {
   1848    cstrlen = ::Int32ToCString(&cbuf, v.toInt32());
   1849    cstr = cbuf.sbuf;
   1850  } else {
   1851    cstr = NumberToCString(&cbuf, v.toDouble(), &cstrlen);
   1852  }
   1853  MOZ_ASSERT(cstr);
   1854  MOZ_ASSERT(cstrlen == strlen(cstr));
   1855 
   1856  MOZ_ASSERT(cstrlen < std::size(cbuf.sbuf));
   1857  return sb.append(cstr, cstrlen);
   1858 }
   1859 
   1860 template <typename CharT>
   1861 inline double CharToNumber(CharT c) {
   1862  if ('0' <= c && c <= '9') {
   1863    return c - '0';
   1864  }
   1865  if (unicode::IsSpace(c)) {
   1866    return 0.0;
   1867  }
   1868  return GenericNaN();
   1869 }
   1870 
   1871 template <typename CharT>
   1872 inline bool CharsToNonDecimalNumber(const CharT* start, const CharT* end,
   1873                                    double* result) {
   1874  MOZ_ASSERT(end - start >= 2);
   1875  MOZ_ASSERT(start[0] == '0');
   1876 
   1877  int radix = 0;
   1878  if (start[1] == 'b' || start[1] == 'B') {
   1879    radix = 2;
   1880  } else if (start[1] == 'o' || start[1] == 'O') {
   1881    radix = 8;
   1882  } else if (start[1] == 'x' || start[1] == 'X') {
   1883    radix = 16;
   1884  } else {
   1885    return false;
   1886  }
   1887 
   1888  // It's probably a non-decimal number. Accept if there's at least one digit
   1889  // after the 0b|0o|0x, and if no non-whitespace characters follow all the
   1890  // digits.
   1891  const CharT* endptr;
   1892  double d;
   1893  MOZ_ALWAYS_TRUE(GetPrefixIntegerImpl(
   1894      start + 2, end, radix, IntegerSeparatorHandling::None, &endptr, &d));
   1895  if (endptr == start + 2 || SkipSpace(endptr, end) != end) {
   1896    *result = GenericNaN();
   1897  } else {
   1898    *result = d;
   1899  }
   1900  return true;
   1901 }
   1902 
   1903 template <typename CharT>
   1904 double js::CharsToNumber(const CharT* chars, size_t length) {
   1905  if (length == 1) {
   1906    return CharToNumber(chars[0]);
   1907  }
   1908 
   1909  const CharT* end = chars + length;
   1910  const CharT* start = SkipSpace(chars, end);
   1911 
   1912  // ECMA doesn't allow signed non-decimal numbers (bug 273467).
   1913  if (end - start >= 2 && start[0] == '0') {
   1914    double d;
   1915    if (CharsToNonDecimalNumber(start, end, &d)) {
   1916      return d;
   1917    }
   1918  }
   1919 
   1920  /*
   1921   * Note that ECMA doesn't treat a string beginning with a '0' as
   1922   * an octal number here. This works because all such numbers will
   1923   * be interpreted as decimal by js_strtod.  Also, any hex numbers
   1924   * that have made it here (which can only be negative ones) will
   1925   * be treated as 0 without consuming the 'x' by js_strtod.
   1926   */
   1927  const CharT* ep;
   1928  double d = js_strtod(start, end, &ep);
   1929  if (SkipSpace(ep, end) != end) {
   1930    return GenericNaN();
   1931  }
   1932  return d;
   1933 }
   1934 
   1935 template double js::CharsToNumber(const Latin1Char* chars, size_t length);
   1936 
   1937 template double js::CharsToNumber(const char16_t* chars, size_t length);
   1938 
   1939 template <class StringT>
   1940 static double StringToNumberImpl(const StringT* str) {
   1941  if (str->hasIndexValue()) {
   1942    return str->getIndexValue();
   1943  }
   1944 
   1945  AutoCheckCannotGC nogc;
   1946  return str->hasLatin1Chars()
   1947             ? CharsToNumber(str->latin1Chars(nogc), str->length())
   1948             : CharsToNumber(str->twoByteChars(nogc), str->length());
   1949 }
   1950 
   1951 double js::LinearStringToNumber(const JSLinearString* str) {
   1952  return StringToNumberImpl<JSLinearString>(str);
   1953 }
   1954 double js::OffThreadAtomToNumber(const JSOffThreadAtom* str) {
   1955  return StringToNumberImpl<JSOffThreadAtom>(str);
   1956 }
   1957 
   1958 bool js::StringToNumber(JSContext* cx, JSString* str, double* result) {
   1959  JSLinearString* linearStr = str->ensureLinear(cx);
   1960  if (!linearStr) {
   1961    return false;
   1962  }
   1963 
   1964  *result = LinearStringToNumber(linearStr);
   1965  return true;
   1966 }
   1967 
   1968 bool js::StringToNumberPure(JSContext* cx, JSString* str, double* result) {
   1969  // IC Code calls this directly.
   1970  AutoUnsafeCallWithABI unsafe;
   1971 
   1972  if (!StringToNumber(cx, str, result)) {
   1973    cx->recoverFromOutOfMemory();
   1974    return false;
   1975  }
   1976  return true;
   1977 }
   1978 
   1979 JS_PUBLIC_API bool js::ToNumberSlow(JSContext* cx, HandleValue v_,
   1980                                    double* out) {
   1981  RootedValue v(cx, v_);
   1982  MOZ_ASSERT(!v.isNumber());
   1983 
   1984  if (!v.isPrimitive()) {
   1985    if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) {
   1986      return false;
   1987    }
   1988 
   1989    if (v.isNumber()) {
   1990      *out = v.toNumber();
   1991      return true;
   1992    }
   1993  }
   1994  if (v.isString()) {
   1995    return StringToNumber(cx, v.toString(), out);
   1996  }
   1997  if (v.isBoolean()) {
   1998    *out = v.toBoolean() ? 1.0 : 0.0;
   1999    return true;
   2000  }
   2001  if (v.isNull()) {
   2002    *out = 0.0;
   2003    return true;
   2004  }
   2005  if (v.isUndefined()) {
   2006    *out = GenericNaN();
   2007    return true;
   2008  }
   2009 
   2010  MOZ_ASSERT(v.isSymbol() || v.isBigInt());
   2011  unsigned errnum = JSMSG_SYMBOL_TO_NUMBER;
   2012  if (v.isBigInt()) {
   2013    errnum = JSMSG_BIGINT_TO_NUMBER;
   2014  }
   2015  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errnum);
   2016  return false;
   2017 }
   2018 
   2019 // BigInt proposal section 3.1.6
   2020 bool js::ToNumericSlow(JSContext* cx, MutableHandleValue vp) {
   2021  MOZ_ASSERT(!vp.isNumeric());
   2022 
   2023  // Step 1.
   2024  if (!vp.isPrimitive()) {
   2025    if (!ToPrimitive(cx, JSTYPE_NUMBER, vp)) {
   2026      return false;
   2027    }
   2028  }
   2029 
   2030  // Step 2.
   2031  if (vp.isBigInt()) {
   2032    return true;
   2033  }
   2034 
   2035  // Step 3.
   2036  return ToNumber(cx, vp);
   2037 }
   2038 
   2039 /*
   2040 * Convert a value to an int8_t, according to the WebIDL rules for byte
   2041 * conversion. Return converted value in *out on success, false on failure.
   2042 */
   2043 JS_PUBLIC_API bool js::ToInt8Slow(JSContext* cx, const HandleValue v,
   2044                                  int8_t* out) {
   2045  MOZ_ASSERT(!v.isInt32());
   2046  double d;
   2047  if (v.isDouble()) {
   2048    d = v.toDouble();
   2049  } else {
   2050    if (!ToNumberSlow(cx, v, &d)) {
   2051      return false;
   2052    }
   2053  }
   2054  *out = ToInt8(d);
   2055  return true;
   2056 }
   2057 
   2058 /*
   2059 * Convert a value to an uint8_t, according to the ToUInt8() function in ES6
   2060 * ECMA-262, 7.1.10. Return converted value in *out on success, false on
   2061 * failure.
   2062 */
   2063 JS_PUBLIC_API bool js::ToUint8Slow(JSContext* cx, const HandleValue v,
   2064                                   uint8_t* out) {
   2065  MOZ_ASSERT(!v.isInt32());
   2066  double d;
   2067  if (v.isDouble()) {
   2068    d = v.toDouble();
   2069  } else {
   2070    if (!ToNumberSlow(cx, v, &d)) {
   2071      return false;
   2072    }
   2073  }
   2074  *out = ToUint8(d);
   2075  return true;
   2076 }
   2077 
   2078 /*
   2079 * Convert a value to an int16_t, according to the WebIDL rules for short
   2080 * conversion. Return converted value in *out on success, false on failure.
   2081 */
   2082 JS_PUBLIC_API bool js::ToInt16Slow(JSContext* cx, const HandleValue v,
   2083                                   int16_t* out) {
   2084  MOZ_ASSERT(!v.isInt32());
   2085  double d;
   2086  if (v.isDouble()) {
   2087    d = v.toDouble();
   2088  } else {
   2089    if (!ToNumberSlow(cx, v, &d)) {
   2090      return false;
   2091    }
   2092  }
   2093  *out = ToInt16(d);
   2094  return true;
   2095 }
   2096 
   2097 /*
   2098 * Convert a value to an int64_t, according to the WebIDL rules for long long
   2099 * conversion. Return converted value in *out on success, false on failure.
   2100 */
   2101 JS_PUBLIC_API bool js::ToInt64Slow(JSContext* cx, const HandleValue v,
   2102                                   int64_t* out) {
   2103  MOZ_ASSERT(!v.isInt32());
   2104  double d;
   2105  if (v.isDouble()) {
   2106    d = v.toDouble();
   2107  } else {
   2108    if (!ToNumberSlow(cx, v, &d)) {
   2109      return false;
   2110    }
   2111  }
   2112  *out = ToInt64(d);
   2113  return true;
   2114 }
   2115 
   2116 /*
   2117 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned
   2118 * long long conversion. Return converted value in *out on success, false on
   2119 * failure.
   2120 */
   2121 JS_PUBLIC_API bool js::ToUint64Slow(JSContext* cx, const HandleValue v,
   2122                                    uint64_t* out) {
   2123  MOZ_ASSERT(!v.isInt32());
   2124  double d;
   2125  if (v.isDouble()) {
   2126    d = v.toDouble();
   2127  } else {
   2128    if (!ToNumberSlow(cx, v, &d)) {
   2129      return false;
   2130    }
   2131  }
   2132  *out = ToUint64(d);
   2133  return true;
   2134 }
   2135 
   2136 JS_PUBLIC_API bool js::ToInt32Slow(JSContext* cx, const HandleValue v,
   2137                                   int32_t* out) {
   2138  MOZ_ASSERT(!v.isInt32());
   2139  double d;
   2140  if (v.isDouble()) {
   2141    d = v.toDouble();
   2142  } else {
   2143    if (!ToNumberSlow(cx, v, &d)) {
   2144      return false;
   2145    }
   2146  }
   2147  *out = ToInt32(d);
   2148  return true;
   2149 }
   2150 
   2151 bool js::ToInt32OrBigIntSlow(JSContext* cx, MutableHandleValue vp) {
   2152  MOZ_ASSERT(!vp.isInt32());
   2153  if (vp.isDouble()) {
   2154    vp.setInt32(ToInt32(vp.toDouble()));
   2155    return true;
   2156  }
   2157 
   2158  if (!ToNumeric(cx, vp)) {
   2159    return false;
   2160  }
   2161 
   2162  if (vp.isBigInt()) {
   2163    return true;
   2164  }
   2165 
   2166  vp.setInt32(ToInt32(vp.toNumber()));
   2167  return true;
   2168 }
   2169 
   2170 JS_PUBLIC_API bool js::ToUint32Slow(JSContext* cx, const HandleValue v,
   2171                                    uint32_t* out) {
   2172  MOZ_ASSERT(!v.isInt32());
   2173  double d;
   2174  if (v.isDouble()) {
   2175    d = v.toDouble();
   2176  } else {
   2177    if (!ToNumberSlow(cx, v, &d)) {
   2178      return false;
   2179    }
   2180  }
   2181  *out = ToUint32(d);
   2182  return true;
   2183 }
   2184 
   2185 JS_PUBLIC_API bool js::ToUint16Slow(JSContext* cx, const HandleValue v,
   2186                                    uint16_t* out) {
   2187  MOZ_ASSERT(!v.isInt32());
   2188  double d;
   2189  if (v.isDouble()) {
   2190    d = v.toDouble();
   2191  } else if (!ToNumberSlow(cx, v, &d)) {
   2192    return false;
   2193  }
   2194  *out = ToUint16(d);
   2195  return true;
   2196 }
   2197 
   2198 // ES2017 draft 7.1.17 ToIndex
   2199 bool js::ToIndexSlow(JSContext* cx, JS::HandleValue v,
   2200                     const unsigned errorNumber, uint64_t* index) {
   2201  MOZ_ASSERT_IF(v.isInt32(), v.toInt32() < 0);
   2202 
   2203  // Step 1.
   2204  if (v.isUndefined()) {
   2205    *index = 0;
   2206    return true;
   2207  }
   2208 
   2209  // Step 2.a.
   2210  double integerIndex;
   2211  if (!ToInteger(cx, v, &integerIndex)) {
   2212    return false;
   2213  }
   2214 
   2215  // Inlined version of ToLength.
   2216  // 1. Already an integer.
   2217  // 2. Step eliminates < 0, +0 == -0 with SameValueZero.
   2218  // 3/4. Limit to <= 2^53-1, so everything above should fail.
   2219  if (integerIndex < 0 || integerIndex >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
   2220    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
   2221    return false;
   2222  }
   2223 
   2224  // Step 3.
   2225  *index = uint64_t(integerIndex);
   2226  return true;
   2227 }
   2228 
   2229 /**
   2230 * Convert |value| to an integer and clamp it to a valid integer index within
   2231 * the range `[0..length]`.
   2232 */
   2233 template <typename ArrayLength>
   2234 bool js::ToIntegerIndexSlow(JSContext* cx, Handle<Value> value,
   2235                            ArrayLength length, ArrayLength* result) {
   2236  MOZ_ASSERT(!value.isInt32());
   2237 
   2238  double relative;
   2239  if (!ToInteger(cx, value, &relative)) {
   2240    return false;
   2241  }
   2242 
   2243  if (relative >= 0) {
   2244    *result = ArrayLength(std::min(relative, double(length)));
   2245  } else {
   2246    *result = ArrayLength(std::max(relative + double(length), 0.0));
   2247  }
   2248  return true;
   2249 }
   2250 
   2251 // Dummy type used when `size_t` is the same type as `uint64_t`. Implements
   2252 // constructor and conversion methods called in ToIntegerIndexSlow.
   2253 struct Dummy {
   2254  explicit Dummy(double) {}
   2255  explicit operator double() { return 0; }
   2256 };
   2257 
   2258 // Instantiate ToIntegerIndexSlow for `size_t` and `uint64_t`, but avoid a
   2259 // duplicate instantiation error for the case when `size_t` is the same type as
   2260 // `uint64_t`.
   2261 
   2262 using Uint64OrDummy =
   2263    std::conditional_t<!std::is_same_v<uint64_t, size_t>, uint64_t, Dummy>;
   2264 
   2265 template bool js::ToIntegerIndexSlow<size_t>(JSContext*, Handle<Value>, size_t,
   2266                                             size_t*);
   2267 
   2268 template bool js::ToIntegerIndexSlow<Uint64OrDummy>(JSContext*, Handle<Value>,
   2269                                                    Uint64OrDummy,
   2270                                                    Uint64OrDummy*);
   2271 
   2272 template <typename CharT>
   2273 double js_strtod(const CharT* begin, const CharT* end, const CharT** dEnd) {
   2274  const CharT* s = SkipSpace(begin, end);
   2275  size_t length = end - s;
   2276 
   2277  {
   2278    // StringToDouble can make indirect calls but can't trigger a GC.
   2279    JS::AutoSuppressGCAnalysis nogc;
   2280 
   2281    using SToDConverter = double_conversion::StringToDoubleConverter;
   2282    SToDConverter converter(SToDConverter::ALLOW_TRAILING_JUNK,
   2283                            /* empty_string_value = */ 0.0,
   2284                            /* junk_string_value = */ GenericNaN(),
   2285                            /* infinity_symbol = */ nullptr,
   2286                            /* nan_symbol = */ nullptr);
   2287    int lengthInt = mozilla::AssertedCast<int>(length);
   2288    double d;
   2289    int processed = 0;
   2290    if constexpr (std::is_same_v<CharT, char16_t>) {
   2291      d = converter.StringToDouble(reinterpret_cast<const uc16*>(s), lengthInt,
   2292                                   &processed);
   2293    } else {
   2294      static_assert(std::is_same_v<CharT, Latin1Char>);
   2295      d = converter.StringToDouble(reinterpret_cast<const char*>(s), lengthInt,
   2296                                   &processed);
   2297    }
   2298    MOZ_ASSERT(processed >= 0);
   2299    MOZ_ASSERT(processed <= lengthInt);
   2300 
   2301    if (processed > 0) {
   2302      *dEnd = s + processed;
   2303      return d;
   2304    }
   2305  }
   2306 
   2307  // Try to parse +Infinity, -Infinity or Infinity. Note that we do this here
   2308  // instead of using StringToDoubleConverter's infinity_symbol because it's
   2309  // faster: the code below is less generic and not on the fast path for regular
   2310  // doubles.
   2311  static constexpr std::string_view Infinity = "Infinity";
   2312  if (length >= Infinity.length()) {
   2313    const CharT* afterSign = s;
   2314    bool negative = (*afterSign == '-');
   2315    if (negative || *afterSign == '+') {
   2316      afterSign++;
   2317    }
   2318    MOZ_ASSERT(afterSign < end);
   2319    if (*afterSign == 'I' && size_t(end - afterSign) >= Infinity.length() &&
   2320        EqualChars(afterSign, Infinity.data(), Infinity.length())) {
   2321      *dEnd = afterSign + Infinity.length();
   2322      return negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
   2323    }
   2324  }
   2325 
   2326  *dEnd = begin;
   2327  return 0.0;
   2328 }
   2329 
   2330 template double js_strtod(const char16_t* begin, const char16_t* end,
   2331                          const char16_t** dEnd);
   2332 
   2333 template double js_strtod(const Latin1Char* begin, const Latin1Char* end,
   2334                          const Latin1Char** dEnd);