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);