tor-browser

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

jsnum.h (15661B)


      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 #ifndef jsnum_h
      8 #define jsnum_h
      9 
     10 #include "mozilla/FloatingPoint.h"
     11 #include "mozilla/Range.h"
     12 #include "mozilla/Utf8.h"
     13 
     14 #include <limits>
     15 
     16 #include "NamespaceImports.h"
     17 
     18 #include "js/Conversions.h"
     19 #include "js/friend/ErrorMessages.h"  // JSMSG_*
     20 
     21 #include "vm/StringType.h"
     22 
     23 namespace js {
     24 
     25 namespace frontend {
     26 class ParserAtomsTable;
     27 class TaggedParserAtomIndex;
     28 }  // namespace frontend
     29 
     30 class GlobalObject;
     31 class StringBuilder;
     32 
     33 [[nodiscard]] extern bool InitRuntimeNumberState(JSRuntime* rt);
     34 
     35 // This is a no-op if built with JS_HAS_INTL_API.
     36 extern void FinishRuntimeNumberState(JSRuntime* rt);
     37 
     38 /*
     39 * This function implements ToString() as specified by ECMA-262-5 section 9.8.1;
     40 * but note that it handles integers specially for performance.
     41 * See also js::NumberToCString().
     42 */
     43 template <AllowGC allowGC>
     44 extern JSString* NumberToString(JSContext* cx, double d);
     45 
     46 extern JSString* NumberToStringPure(JSContext* cx, double d);
     47 
     48 extern JSAtom* NumberToAtom(JSContext* cx, double d);
     49 
     50 frontend::TaggedParserAtomIndex NumberToParserAtom(
     51    FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, double d);
     52 
     53 template <AllowGC allowGC>
     54 extern JSLinearString* Int32ToString(JSContext* cx, int32_t i);
     55 
     56 template <AllowGC allowGC>
     57 extern JSLinearString* Int32ToStringWithHeap(JSContext* cx, int32_t i,
     58                                             gc::Heap heap);
     59 
     60 extern JSLinearString* Int32ToStringPure(JSContext* cx, int32_t i);
     61 
     62 template <AllowGC allowGC>
     63 extern JSLinearString* Int32ToStringWithBase(JSContext* cx, int32_t i,
     64                                             int32_t base, bool lowerCase);
     65 
     66 extern JSAtom* Int32ToAtom(JSContext* cx, int32_t si);
     67 
     68 frontend::TaggedParserAtomIndex Int32ToParserAtom(
     69    FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, int32_t si);
     70 
     71 // ES6 15.7.3.12
     72 extern bool IsInteger(double d);
     73 
     74 /*
     75 * Convert an integer or double (contained in the given value) to a string and
     76 * append to the given string builder.
     77 */
     78 [[nodiscard]] extern bool NumberValueToStringBuilder(const Value& v,
     79                                                     StringBuilder& sb);
     80 
     81 extern JSLinearString* IndexToString(JSContext* cx, uint32_t index);
     82 
     83 struct ToCStringBuf {
     84  char sbuf[JS::MaximumNumberToStringLength] = {};
     85 };
     86 
     87 struct Int32ToCStringBuf {
     88  // The amount of space large enough to store the null-terminated result of
     89  // |ToString| on any int32.
     90  //
     91  // We use the same amount for uint32 (base 10 and base 16), even though uint32
     92  // only need 11 characters (base 10) resp. 9 characters (base 16) instead of
     93  // 12 characters for int32 in base 10.
     94  static constexpr size_t MaximumInt32ToStringLength =
     95      std::numeric_limits<int32_t>::digits10 +
     96      1 +  // account for the largest possible int32 value
     97      1 +  // sign for negative numbers
     98      1    // null character
     99      ;
    100 
    101  char sbuf[MaximumInt32ToStringLength] = {};
    102 };
    103 
    104 // Convert a number to a C string.  This function implements ToString() as
    105 // specified by ECMA-262-5 section 9.8.1.  It handles integral values cheaply.
    106 // Infallible: always returns a non-nullptr string.
    107 // The optional `length` out-param is set to the string length of the result.
    108 extern char* NumberToCString(ToCStringBuf* cbuf, double d,
    109                             size_t* length = nullptr);
    110 
    111 extern char* Int32ToCString(Int32ToCStringBuf* cbuf, int32_t value,
    112                            size_t* length = nullptr);
    113 
    114 extern char* Uint32ToCString(Int32ToCStringBuf* cbuf, uint32_t value,
    115                             size_t* length = nullptr);
    116 
    117 // Like NumberToCString, but accepts only unsigned integers and uses base 16.
    118 // Infallible: always returns a non-nullptr string.
    119 // The optional `length` out-param is set to the string length of the result.
    120 extern char* Uint32ToHexCString(Int32ToCStringBuf* cbuf, uint32_t value,
    121                                size_t* length = nullptr);
    122 
    123 /*
    124 * The largest positive integer such that all positive integers less than it
    125 * may be precisely represented using the IEEE-754 double-precision format.
    126 */
    127 constexpr double DOUBLE_INTEGRAL_PRECISION_LIMIT = uint64_t(1) << 53;
    128 
    129 /*
    130 * The smallest positive double such that all positive doubles larger or equal
    131 * than it have an exact decimal representation without exponential form.
    132 */
    133 constexpr double DOUBLE_DECIMAL_IN_SHORTEST_LOW = 1.0e-6;
    134 
    135 /*
    136 * The largest positive double such that all positive doubles less than it
    137 * have an exact decimal representation without exponential form.
    138 */
    139 constexpr double DOUBLE_DECIMAL_IN_SHORTEST_HIGH = 1.0e21;
    140 
    141 /*
    142 * Parse a decimal number encoded in |chars|.  The decimal number must be
    143 * sufficiently small that it will not overflow the integrally-precise range of
    144 * the double type -- that is, the number will be smaller than
    145 * DOUBLE_INTEGRAL_PRECISION_LIMIT
    146 */
    147 template <typename CharT>
    148 extern double ParseDecimalNumber(const mozilla::Range<const CharT> chars);
    149 
    150 enum class IntegerSeparatorHandling : bool { None, SkipUnderscore };
    151 
    152 /*
    153 * Compute the positive integer of the given base described immediately at the
    154 * start of the range [start, end) -- no whitespace-skipping, no magical
    155 * leading-"0" octal or leading-"0x" hex behavior, no "+"/"-" parsing, just
    156 * reading the digits of the integer.  Return the index one past the end of the
    157 * digits of the integer in *endp, and return the integer itself in *dp.  If
    158 * base is 10 or a power of two the returned integer is the closest possible
    159 * double; otherwise extremely large integers may be slightly inaccurate.
    160 *
    161 * The |separatorHandling| controls whether or not numeric separators can be
    162 * part of integer string. If the option is enabled, all '_' characters in the
    163 * string are ignored. Underscore characters must not appear directly next to
    164 * each other, e.g. '1__2' will lead to an assertion.
    165 *
    166 * If [start, end) does not begin with a number with the specified base,
    167 * *dp == 0 and *endp == start upon return.
    168 */
    169 template <typename CharT>
    170 [[nodiscard]] extern bool GetPrefixInteger(
    171    const CharT* start, const CharT* end, int base,
    172    IntegerSeparatorHandling separatorHandling, const CharT** endp, double* dp);
    173 
    174 inline const char16_t* ToRawChars(const char16_t* units) { return units; }
    175 
    176 inline const unsigned char* ToRawChars(const unsigned char* units) {
    177  return units;
    178 }
    179 
    180 inline const unsigned char* ToRawChars(const mozilla::Utf8Unit* units) {
    181  return mozilla::Utf8AsUnsignedChars(units);
    182 }
    183 
    184 /**
    185 * Like GetPrefixInteger, but [start, end) must all be digits in the given
    186 * base (and so this function doesn't take a useless outparam).
    187 */
    188 template <typename CharT>
    189 [[nodiscard]] extern bool GetFullInteger(
    190    const CharT* start, const CharT* end, int base,
    191    IntegerSeparatorHandling separatorHandling, double* dp) {
    192  decltype(ToRawChars(start)) realEnd;
    193  if (GetPrefixInteger(ToRawChars(start), ToRawChars(end), base,
    194                       separatorHandling, &realEnd, dp)) {
    195    MOZ_ASSERT(end == static_cast<const void*>(realEnd));
    196    return true;
    197  }
    198  return false;
    199 }
    200 
    201 /*
    202 * This is like GetPrefixInteger, but only deals with base 10, always ignores
    203 * '_', and doesn't have an |endp| outparam. It should only be used when the
    204 * characters are known to match |DecimalIntegerLiteral|, cf. ES2020, 11.8.3
    205 * Numeric Literals.
    206 */
    207 template <typename CharT>
    208 [[nodiscard]] extern bool GetDecimalInteger(const CharT* start,
    209                                            const CharT* end, double* dp);
    210 
    211 /*
    212 * This is like GetDecimalInteger, but also allows non-integer numbers. It
    213 * should only be used when the characters are known to match |DecimalLiteral|,
    214 * cf. ES2020, 11.8.3 Numeric Literals.
    215 */
    216 template <typename CharT>
    217 [[nodiscard]] extern bool GetDecimal(const CharT* start, const CharT* end,
    218                                     double* dp);
    219 
    220 template <typename CharT>
    221 double CharsToNumber(const CharT* chars, size_t length);
    222 
    223 [[nodiscard]] extern bool StringToNumber(JSContext* cx, JSString* str,
    224                                         double* result);
    225 
    226 [[nodiscard]] extern bool StringToNumberPure(JSContext* cx, JSString* str,
    227                                             double* result);
    228 
    229 // Infallible version of StringToNumber for linear strings.
    230 extern double LinearStringToNumber(const JSLinearString* str);
    231 
    232 extern double OffThreadAtomToNumber(const JSOffThreadAtom* str);
    233 
    234 // Parse the input string as if Number.parseInt had been called.
    235 extern bool NumberParseInt(JSContext* cx, JS::HandleString str, int32_t radix,
    236                           JS::MutableHandleValue result);
    237 
    238 /* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */
    239 [[nodiscard]] MOZ_ALWAYS_INLINE bool ToNumber(JSContext* cx,
    240                                              JS::MutableHandleValue vp) {
    241  if (vp.isNumber()) {
    242    return true;
    243  }
    244  double d;
    245  extern JS_PUBLIC_API bool ToNumberSlow(JSContext * cx, HandleValue v,
    246                                         double* dp);
    247  if (!ToNumberSlow(cx, vp, &d)) {
    248    return false;
    249  }
    250 
    251  vp.setNumber(d);
    252  return true;
    253 }
    254 
    255 bool ToNumericSlow(JSContext* cx, JS::MutableHandleValue vp);
    256 
    257 // BigInt proposal section 3.1.6
    258 [[nodiscard]] MOZ_ALWAYS_INLINE bool ToNumeric(JSContext* cx,
    259                                               JS::MutableHandleValue vp) {
    260  if (vp.isNumeric()) {
    261    return true;
    262  }
    263  return ToNumericSlow(cx, vp);
    264 }
    265 
    266 bool ToInt32OrBigIntSlow(JSContext* cx, JS::MutableHandleValue vp);
    267 
    268 [[nodiscard]] MOZ_ALWAYS_INLINE bool ToInt32OrBigInt(
    269    JSContext* cx, JS::MutableHandleValue vp) {
    270  if (vp.isInt32()) {
    271    return true;
    272  }
    273  return ToInt32OrBigIntSlow(cx, vp);
    274 }
    275 
    276 } /* namespace js */
    277 
    278 /*
    279 * Similar to strtod except that it replaces overflows with infinities of the
    280 * correct sign, and underflows with zeros of the correct sign.  Guaranteed to
    281 * return the closest double number to the given input.
    282 *
    283 * Also allows inputs of the form [+|-]Infinity, which produce an infinity of
    284 * the appropriate sign.  The case of the "Infinity" string must match exactly.
    285 * If the string does not contain a number, set *dEnd to begin and return 0.0.
    286 */
    287 template <typename CharT>
    288 [[nodiscard]] extern double js_strtod(const CharT* begin, const CharT* end,
    289                                      const CharT** dEnd);
    290 
    291 namespace js {
    292 
    293 /**
    294 * Like js_strtod, but for when the number always constitutes the entire range
    295 * (and so |dEnd| would be a value already known).
    296 */
    297 template <typename CharT>
    298 [[nodiscard]] extern double FullStringToDouble(const CharT* begin,
    299                                               const CharT* end) {
    300  decltype(ToRawChars(begin)) realEnd;
    301  double d = js_strtod(ToRawChars(begin), ToRawChars(end), &realEnd);
    302  MOZ_ASSERT(end == static_cast<const void*>(realEnd));
    303  return d;
    304 }
    305 
    306 [[nodiscard]] extern bool num_valueOf(JSContext* cx, unsigned argc, Value* vp);
    307 
    308 static inline bool IsNumberIndex(const Value& v) {
    309  if (v.isInt32() && v.toInt32() >= 0) {
    310    return true;
    311  }
    312 
    313  int64_t i;
    314  if (v.isDouble() && mozilla::NumberEqualsInt64(v.toDouble(), &i) && i >= 0 &&
    315      i <= MAX_ARRAY_INDEX) {
    316    return true;
    317  }
    318 
    319  return false;
    320 }
    321 
    322 /*
    323 * Returns true if the given value is definitely an index: that is, the value
    324 * is a number that's an unsigned 32-bit integer.
    325 *
    326 * This method prioritizes common-case speed over accuracy in every case.  It
    327 * can produce false negatives (but not false positives): some values which are
    328 * indexes will be reported not to be indexes by this method.  Users must
    329 * consider this possibility when using this method.
    330 */
    331 static MOZ_ALWAYS_INLINE bool IsDefinitelyIndex(const Value& v,
    332                                                uint32_t* indexp) {
    333  if (v.isInt32() && v.toInt32() >= 0) {
    334    *indexp = v.toInt32();
    335    return true;
    336  }
    337 
    338  int32_t i;
    339  if (v.isDouble() && mozilla::NumberEqualsInt32(v.toDouble(), &i) && i >= 0) {
    340    *indexp = uint32_t(i);
    341    return true;
    342  }
    343 
    344  if (v.isString() && v.toString()->hasIndexValue()) {
    345    *indexp = v.toString()->getIndexValue();
    346    return true;
    347  }
    348 
    349  return false;
    350 }
    351 
    352 // ES2020 draft rev 6b05bc56ba4e3c7a2b9922c4282d9eb844426d9b
    353 // 7.1.5 ToInteger ( argument )
    354 [[nodiscard]] static inline bool ToInteger(JSContext* cx, HandleValue v,
    355                                           double* dp) {
    356  if (v.isInt32()) {
    357    *dp = v.toInt32();
    358    return true;
    359  }
    360  if (v.isDouble()) {
    361    *dp = v.toDouble();
    362  } else if (v.isString() && v.toString()->hasIndexValue()) {
    363    *dp = v.toString()->getIndexValue();
    364    return true;
    365  } else {
    366    extern JS_PUBLIC_API bool ToNumberSlow(JSContext * cx, HandleValue v,
    367                                           double* dp);
    368    if (!ToNumberSlow(cx, v, dp)) {
    369      return false;
    370    }
    371  }
    372  *dp = JS::ToInteger(*dp);
    373  return true;
    374 }
    375 
    376 /* ES2017 draft 7.1.17 ToIndex
    377 *
    378 * Return true and set |*index| to the integer value if |v| is a valid
    379 * integer index value. Otherwise report a RangeError and return false.
    380 *
    381 * The returned index will always be in the range 0 <= *index <= 2^53-1.
    382 */
    383 [[nodiscard]] extern bool ToIndexSlow(JSContext* cx, JS::HandleValue v,
    384                                      const unsigned errorNumber,
    385                                      uint64_t* index);
    386 
    387 [[nodiscard]] static inline bool ToIndex(JSContext* cx, JS::HandleValue v,
    388                                         const unsigned errorNumber,
    389                                         uint64_t* index) {
    390  if (v.isInt32()) {
    391    int32_t i = v.toInt32();
    392    if (i >= 0) {
    393      *index = uint64_t(i);
    394      return true;
    395    }
    396  }
    397  return ToIndexSlow(cx, v, errorNumber, index);
    398 }
    399 
    400 [[nodiscard]] static inline bool ToIndex(JSContext* cx, JS::HandleValue v,
    401                                         uint64_t* index) {
    402  return ToIndex(cx, v, JSMSG_BAD_INDEX, index);
    403 }
    404 
    405 /**
    406 * Convert |value| to an integer and clamp it to a valid integer index within
    407 * the range `[0..length]`.
    408 */
    409 template <typename ArrayLength>
    410 [[nodiscard]] extern bool ToIntegerIndexSlow(JSContext* cx, Handle<Value> value,
    411                                             ArrayLength length,
    412                                             ArrayLength* result);
    413 
    414 template <typename ArrayLength>
    415 [[nodiscard]] static inline bool ToIntegerIndex(JSContext* cx,
    416                                                Handle<Value> value,
    417                                                ArrayLength length,
    418                                                ArrayLength* result) {
    419  static_assert(std::is_unsigned_v<ArrayLength>);
    420 
    421  // Optimize for the common case when |value| is an int32 to avoid unnecessary
    422  // floating point computations.
    423  if (value.isInt32()) {
    424    int32_t relative = value.toInt32();
    425 
    426    if (relative >= 0) {
    427      *result = std::min(ArrayLength(relative), length);
    428    } else if (mozilla::Abs(relative) <= length) {
    429      *result = length - mozilla::Abs(relative);
    430    } else {
    431      *result = 0;
    432    }
    433    return true;
    434  }
    435 
    436  return ToIntegerIndexSlow(cx, value, length, result);
    437 }
    438 
    439 static inline size_t ToIntegerIndex(intptr_t index, size_t length) {
    440  static_assert(std::is_same_v<size_t, uintptr_t>,
    441                "expect size_t being equal to uintptr_t");
    442 
    443  if (index >= 0) {
    444    return std::min(size_t(index), length);
    445  }
    446  if (mozilla::Abs(index) <= length) {
    447    return length - mozilla::Abs(index);
    448  }
    449  return 0;
    450 }
    451 
    452 } /* namespace js */
    453 
    454 #endif /* jsnum_h */