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