Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / jsnum.cpp
blobb51bac8390db98d19792ff8835cfa7220a89873a
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/. */
7 /*
8 * JS number type and wrapper class.
9 */
11 #include "jsnum.h"
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"
20 #include <algorithm>
21 #include <iterator>
22 #include <limits>
23 #ifdef HAVE_LOCALECONV
24 # include <locale.h>
25 #endif
26 #include <math.h>
27 #include <string.h> // memmove
28 #include <string_view>
30 #include "jstypes.h"
32 #include "builtin/String.h"
33 #include "double-conversion/double-conversion.h"
34 #include "frontend/ParserAtom.h" // frontend::{ParserAtomsTable, TaggedParserAtomIndex}
35 #include "jit/InlinableNatives.h"
36 #include "js/CharacterEncoding.h"
37 #include "js/Conversions.h"
38 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
39 #include "js/GCAPI.h"
40 #if !JS_HAS_INTL_API
41 # include "js/LocaleSensitive.h"
42 #endif
43 #include "js/PropertyAndElement.h" // JS_DefineFunctions
44 #include "js/PropertySpec.h"
45 #include "util/DoubleToString.h"
46 #include "util/Memory.h"
47 #include "util/StringBuffer.h"
48 #include "vm/BigIntType.h"
49 #include "vm/GlobalObject.h"
50 #include "vm/JSAtomUtils.h" // Atomize, AtomizeString
51 #include "vm/JSContext.h"
52 #include "vm/JSObject.h"
53 #include "vm/StaticStrings.h"
55 #include "vm/Compartment-inl.h" // For js::UnwrapAndTypeCheckThis
56 #include "vm/GeckoProfiler-inl.h"
57 #include "vm/JSAtomUtils-inl.h" // BackfillIndexInCharBuffer
58 #include "vm/NativeObject-inl.h"
59 #include "vm/NumberObject-inl.h"
60 #include "vm/StringType-inl.h"
62 using namespace js;
64 using mozilla::Abs;
65 using mozilla::AsciiAlphanumericToNumber;
66 using mozilla::IsAsciiAlphanumeric;
67 using mozilla::IsAsciiDigit;
68 using mozilla::Maybe;
69 using mozilla::MinNumberValue;
70 using mozilla::NegativeInfinity;
71 using mozilla::NumberEqualsInt32;
72 using mozilla::PositiveInfinity;
73 using mozilla::RangedPtr;
74 using mozilla::Utf8AsUnsignedChars;
75 using mozilla::Utf8Unit;
77 using JS::AutoCheckCannotGC;
78 using JS::GenericNaN;
79 using JS::ToInt16;
80 using JS::ToInt32;
81 using JS::ToInt64;
82 using JS::ToInt8;
83 using JS::ToUint16;
84 using JS::ToUint32;
85 using JS::ToUint64;
86 using JS::ToUint8;
88 static bool EnsureDtoaState(JSContext* cx) {
89 if (!cx->dtoaState) {
90 cx->dtoaState = NewDtoaState();
91 if (!cx->dtoaState) {
92 return false;
95 return true;
98 template <typename CharT>
99 static inline void AssertWellPlacedNumericSeparator(const CharT* s,
100 const CharT* start,
101 const CharT* end) {
102 MOZ_ASSERT(start < end, "string is non-empty");
103 MOZ_ASSERT(s > start, "number can't start with a separator");
104 MOZ_ASSERT(s + 1 < end,
105 "final character in a numeric literal can't be a separator");
106 MOZ_ASSERT(*(s + 1) != '_',
107 "separator can't be followed by another separator");
108 MOZ_ASSERT(*(s - 1) != '_',
109 "separator can't be preceded by another separator");
112 namespace {
114 template <typename CharT>
115 class BinaryDigitReader {
116 const int base; /* Base of number; must be a power of 2 */
117 int digit; /* Current digit value in radix given by base */
118 int digitMask; /* Mask to extract the next bit from digit */
119 const CharT* cur; /* Pointer to the remaining digits */
120 const CharT* start; /* Pointer to the start of the string */
121 const CharT* end; /* Pointer to first non-digit */
123 public:
124 BinaryDigitReader(int base, const CharT* start, const CharT* end)
125 : base(base),
126 digit(0),
127 digitMask(0),
128 cur(start),
129 start(start),
130 end(end) {}
132 /* Return the next binary digit from the number, or -1 if done. */
133 int nextDigit() {
134 if (digitMask == 0) {
135 if (cur == end) {
136 return -1;
139 int c = *cur++;
140 if (c == '_') {
141 AssertWellPlacedNumericSeparator(cur - 1, start, end);
142 c = *cur++;
145 MOZ_ASSERT(IsAsciiAlphanumeric(c));
146 digit = AsciiAlphanumericToNumber(c);
147 digitMask = base >> 1;
150 int bit = (digit & digitMask) != 0;
151 digitMask >>= 1;
152 return bit;
156 } /* anonymous namespace */
159 * The fast result might also have been inaccurate for power-of-two bases. This
160 * happens if the addition in value * 2 + digit causes a round-down to an even
161 * least significant mantissa bit when the first dropped bit is a one. If any
162 * of the following digits in the number (which haven't been added in yet) are
163 * nonzero, then the correct action would have been to round up instead of
164 * down. An example occurs when reading the number 0x1000000000000081, which
165 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
167 template <typename CharT>
168 static double ComputeAccurateBinaryBaseInteger(const CharT* start,
169 const CharT* end, int base) {
170 BinaryDigitReader<CharT> bdr(base, start, end);
172 /* Skip leading zeroes. */
173 int bit;
174 do {
175 bit = bdr.nextDigit();
176 } while (bit == 0);
178 MOZ_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
180 /* Gather the 53 significant bits (including the leading 1). */
181 double value = 1.0;
182 for (int j = 52; j > 0; j--) {
183 bit = bdr.nextDigit();
184 if (bit < 0) {
185 return value;
187 value = value * 2 + bit;
190 /* bit2 is the 54th bit (the first dropped from the mantissa). */
191 int bit2 = bdr.nextDigit();
192 if (bit2 >= 0) {
193 double factor = 2.0;
194 int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
195 int bit3;
197 while ((bit3 = bdr.nextDigit()) >= 0) {
198 sticky |= bit3;
199 factor *= 2;
201 value += bit2 & (bit | sticky);
202 value *= factor;
205 return value;
208 template <typename CharT>
209 double js::ParseDecimalNumber(const mozilla::Range<const CharT> chars) {
210 MOZ_ASSERT(chars.length() > 0);
211 uint64_t dec = 0;
212 RangedPtr<const CharT> s = chars.begin(), end = chars.end();
213 do {
214 CharT c = *s;
215 MOZ_ASSERT('0' <= c && c <= '9');
216 uint8_t digit = c - '0';
217 uint64_t next = dec * 10 + digit;
218 MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
219 "next value won't be an integrally-precise double");
220 dec = next;
221 } while (++s < end);
222 return static_cast<double>(dec);
225 template double js::ParseDecimalNumber(
226 const mozilla::Range<const Latin1Char> chars);
228 template double js::ParseDecimalNumber(
229 const mozilla::Range<const char16_t> chars);
231 template <typename CharT>
232 static bool GetPrefixIntegerImpl(const CharT* start, const CharT* end, int base,
233 IntegerSeparatorHandling separatorHandling,
234 const CharT** endp, double* dp) {
235 MOZ_ASSERT(start <= end);
236 MOZ_ASSERT(2 <= base && base <= 36);
238 const CharT* s = start;
239 double d = 0.0;
240 for (; s < end; s++) {
241 CharT c = *s;
242 if (!IsAsciiAlphanumeric(c)) {
243 if (c == '_' &&
244 separatorHandling == IntegerSeparatorHandling::SkipUnderscore) {
245 AssertWellPlacedNumericSeparator(s, start, end);
246 continue;
248 break;
251 uint8_t digit = AsciiAlphanumericToNumber(c);
252 if (digit >= base) {
253 break;
256 d = d * base + digit;
259 *endp = s;
260 *dp = d;
262 /* If we haven't reached the limit of integer precision, we're done. */
263 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
264 return true;
268 * Otherwise compute the correct integer from the prefix of valid digits
269 * if we're computing for base ten or a power of two. Don't worry about
270 * other bases; see ES2018, 18.2.5 `parseInt(string, radix)`, step 13.
272 if (base == 10) {
273 return false;
276 if ((base & (base - 1)) == 0) {
277 *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
280 return true;
283 template <typename CharT>
284 bool js::GetPrefixInteger(const CharT* start, const CharT* end, int base,
285 IntegerSeparatorHandling separatorHandling,
286 const CharT** endp, double* dp) {
287 if (GetPrefixIntegerImpl(start, end, base, separatorHandling, endp, dp)) {
288 return true;
291 // Can only fail for base 10.
292 MOZ_ASSERT(base == 10);
294 // If we're accumulating a decimal number and the number is >= 2^53, then the
295 // fast result from the loop in GetPrefixIntegerImpl may be inaccurate. Call
296 // GetDecimal to get the correct answer.
297 return GetDecimal(start, *endp, dp);
300 namespace js {
302 template bool GetPrefixInteger(const char16_t* start, const char16_t* end,
303 int base,
304 IntegerSeparatorHandling separatorHandling,
305 const char16_t** endp, double* dp);
307 template bool GetPrefixInteger(const Latin1Char* start, const Latin1Char* end,
308 int base,
309 IntegerSeparatorHandling separatorHandling,
310 const Latin1Char** endp, double* dp);
312 } // namespace js
314 template <typename CharT>
315 bool js::GetDecimalInteger(const CharT* start, const CharT* end, double* dp) {
316 MOZ_ASSERT(start <= end);
318 double d = 0.0;
319 for (const CharT* s = start; s < end; s++) {
320 CharT c = *s;
321 if (c == '_') {
322 AssertWellPlacedNumericSeparator(s, start, end);
323 continue;
325 MOZ_ASSERT(IsAsciiDigit(c));
326 int digit = c - '0';
327 d = d * 10 + digit;
330 // If we haven't reached the limit of integer precision, we're done.
331 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
332 *dp = d;
333 return true;
336 // Otherwise compute the correct integer using GetDecimal.
337 return GetDecimal(start, end, dp);
340 namespace js {
342 template bool GetDecimalInteger(const char16_t* start, const char16_t* end,
343 double* dp);
345 template bool GetDecimalInteger(const Latin1Char* start, const Latin1Char* end,
346 double* dp);
348 template <>
349 bool GetDecimalInteger<Utf8Unit>(const Utf8Unit* start, const Utf8Unit* end,
350 double* dp) {
351 return GetDecimalInteger(Utf8AsUnsignedChars(start), Utf8AsUnsignedChars(end),
352 dp);
355 } // namespace js
357 template <typename CharT>
358 bool js::GetDecimal(const CharT* start, const CharT* end, double* dp) {
359 MOZ_ASSERT(start <= end);
361 size_t length = end - start;
363 auto convert = [](auto* chars, size_t length) -> double {
364 using SToDConverter = double_conversion::StringToDoubleConverter;
365 SToDConverter converter(/* flags = */ 0, /* empty_string_value = */ 0.0,
366 /* junk_string_value = */ 0.0,
367 /* infinity_symbol = */ nullptr,
368 /* nan_symbol = */ nullptr);
369 int lengthInt = mozilla::AssertedCast<int>(length);
370 int processed = 0;
371 double d = converter.StringToDouble(chars, lengthInt, &processed);
372 MOZ_ASSERT(processed >= 0);
373 MOZ_ASSERT(size_t(processed) == length);
374 return d;
377 // If there are no underscores, we don't need to copy the chars.
378 bool hasUnderscore = std::any_of(start, end, [](auto c) { return c == '_'; });
379 if (!hasUnderscore) {
380 if constexpr (std::is_same_v<CharT, char16_t>) {
381 *dp = convert(reinterpret_cast<const uc16*>(start), length);
382 } else {
383 static_assert(std::is_same_v<CharT, Latin1Char>);
384 *dp = convert(reinterpret_cast<const char*>(start), length);
386 return true;
389 Vector<char, 32, SystemAllocPolicy> chars;
390 if (!chars.growByUninitialized(length)) {
391 return false;
394 const CharT* s = start;
395 size_t i = 0;
396 for (; s < end; s++) {
397 CharT c = *s;
398 if (c == '_') {
399 AssertWellPlacedNumericSeparator(s, start, end);
400 continue;
402 MOZ_ASSERT(IsAsciiDigit(c) || c == '.' || c == 'e' || c == 'E' ||
403 c == '+' || c == '-');
404 chars[i++] = char(c);
407 *dp = convert(chars.begin(), i);
408 return true;
411 namespace js {
413 template bool GetDecimal(const char16_t* start, const char16_t* end,
414 double* dp);
416 template bool GetDecimal(const Latin1Char* start, const Latin1Char* end,
417 double* dp);
419 template <>
420 bool GetDecimal<Utf8Unit>(const Utf8Unit* start, const Utf8Unit* end,
421 double* dp) {
422 return GetDecimal(Utf8AsUnsignedChars(start), Utf8AsUnsignedChars(end), dp);
425 } // namespace js
427 static bool num_parseFloat(JSContext* cx, unsigned argc, Value* vp) {
428 CallArgs args = CallArgsFromVp(argc, vp);
430 if (args.length() == 0) {
431 args.rval().setNaN();
432 return true;
435 if (args[0].isNumber()) {
436 // ToString(-0) is "0", handle it accordingly.
437 if (args[0].isDouble() && args[0].toDouble() == 0.0) {
438 args.rval().setInt32(0);
439 } else {
440 args.rval().set(args[0]);
442 return true;
445 JSString* str = ToString<CanGC>(cx, args[0]);
446 if (!str) {
447 return false;
450 if (str->hasIndexValue()) {
451 args.rval().setNumber(str->getIndexValue());
452 return true;
455 JSLinearString* linear = str->ensureLinear(cx);
456 if (!linear) {
457 return false;
460 double d;
461 AutoCheckCannotGC nogc;
462 if (linear->hasLatin1Chars()) {
463 const Latin1Char* begin = linear->latin1Chars(nogc);
464 const Latin1Char* end;
465 d = js_strtod(begin, begin + linear->length(), &end);
466 if (end == begin) {
467 d = GenericNaN();
469 } else {
470 const char16_t* begin = linear->twoByteChars(nogc);
471 const char16_t* end;
472 d = js_strtod(begin, begin + linear->length(), &end);
473 if (end == begin) {
474 d = GenericNaN();
478 args.rval().setDouble(d);
479 return true;
482 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
483 // 19.2.5 parseInt ( string, radix )
484 template <typename CharT>
485 static bool ParseIntImpl(JSContext* cx, const CharT* chars, size_t length,
486 bool stripPrefix, int32_t radix, double* res) {
487 // Step 2.
488 const CharT* end = chars + length;
489 const CharT* s = SkipSpace(chars, end);
491 MOZ_ASSERT(chars <= s);
492 MOZ_ASSERT(s <= end);
494 // Steps 3-4.
495 bool negative = (s != end && s[0] == '-');
497 // Step 5. */
498 if (s != end && (s[0] == '-' || s[0] == '+')) {
499 s++;
502 // Step 10.
503 if (stripPrefix) {
504 if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
505 s += 2;
506 radix = 16;
510 // Steps 11-15.
511 const CharT* actualEnd;
512 double d;
513 if (!js::GetPrefixInteger(s, end, radix, IntegerSeparatorHandling::None,
514 &actualEnd, &d)) {
515 ReportOutOfMemory(cx);
516 return false;
519 if (s == actualEnd) {
520 *res = GenericNaN();
521 } else {
522 *res = negative ? -d : d;
524 return true;
527 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
528 // 19.2.5 parseInt ( string, radix )
529 bool js::NumberParseInt(JSContext* cx, HandleString str, int32_t radix,
530 MutableHandleValue result) {
531 // Step 7.
532 bool stripPrefix = true;
534 // Steps 8-9.
535 if (radix != 0) {
536 if (radix < 2 || radix > 36) {
537 result.setNaN();
538 return true;
541 if (radix != 16) {
542 stripPrefix = false;
544 } else {
545 radix = 10;
547 MOZ_ASSERT(2 <= radix && radix <= 36);
549 JSLinearString* linear = str->ensureLinear(cx);
550 if (!linear) {
551 return false;
554 // Steps 2-5, 10-16.
555 AutoCheckCannotGC nogc;
556 size_t length = linear->length();
557 double number;
558 if (linear->hasLatin1Chars()) {
559 if (!ParseIntImpl(cx, linear->latin1Chars(nogc), length, stripPrefix, radix,
560 &number)) {
561 return false;
563 } else {
564 if (!ParseIntImpl(cx, linear->twoByteChars(nogc), length, stripPrefix,
565 radix, &number)) {
566 return false;
570 result.setNumber(number);
571 return true;
574 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
575 // 19.2.5 parseInt ( string, radix )
576 static bool num_parseInt(JSContext* cx, unsigned argc, Value* vp) {
577 CallArgs args = CallArgsFromVp(argc, vp);
579 /* Fast paths and exceptional cases. */
580 if (args.length() == 0) {
581 args.rval().setNaN();
582 return true;
585 if (args.length() == 1 || (args[1].isInt32() && (args[1].toInt32() == 0 ||
586 args[1].toInt32() == 10))) {
587 if (args[0].isInt32()) {
588 args.rval().set(args[0]);
589 return true;
593 * Step 1 is |inputString = ToString(string)|. When string >=
594 * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
595 * the word, which would mean the result of parseInt(string) should be |N|.
597 * To preserve this behaviour, we can't use the fast-path when string >=
598 * 1e21, or else the result would be |NeM|.
600 * The same goes for values smaller than 1.0e-6, because the string would be
601 * in the form of "Ne-M".
603 if (args[0].isDouble()) {
604 double d = args[0].toDouble();
605 if (DOUBLE_DECIMAL_IN_SHORTEST_LOW <= d &&
606 d < DOUBLE_DECIMAL_IN_SHORTEST_HIGH) {
607 args.rval().setNumber(floor(d));
608 return true;
610 if (-DOUBLE_DECIMAL_IN_SHORTEST_HIGH < d &&
611 d <= -DOUBLE_DECIMAL_IN_SHORTEST_LOW) {
612 args.rval().setNumber(-floor(-d));
613 return true;
615 if (d == 0.0) {
616 args.rval().setInt32(0);
617 return true;
621 if (args[0].isString()) {
622 JSString* str = args[0].toString();
623 if (str->hasIndexValue()) {
624 args.rval().setNumber(str->getIndexValue());
625 return true;
630 // Step 1.
631 RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
632 if (!inputString) {
633 return false;
636 // Step 6.
637 int32_t radix = 0;
638 if (args.hasDefined(1)) {
639 if (!ToInt32(cx, args[1], &radix)) {
640 return false;
644 // Steps 2-5, 7-16.
645 return NumberParseInt(cx, inputString, radix, args.rval());
648 static const JSFunctionSpec number_functions[] = {
649 JS_SELF_HOSTED_FN("isNaN", "Global_isNaN", 1, JSPROP_RESOLVING),
650 JS_SELF_HOSTED_FN("isFinite", "Global_isFinite", 1, JSPROP_RESOLVING),
651 JS_FS_END};
653 const JSClass NumberObject::class_ = {
654 "Number",
655 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
656 JS_NULL_CLASS_OPS, &NumberObject::classSpec_};
658 static bool Number(JSContext* cx, unsigned argc, Value* vp) {
659 CallArgs args = CallArgsFromVp(argc, vp);
661 if (args.length() > 0) {
662 // BigInt proposal section 6.2, steps 2a-c.
663 if (!ToNumeric(cx, args[0])) {
664 return false;
666 if (args[0].isBigInt()) {
667 args[0].setNumber(BigInt::numberValue(args[0].toBigInt()));
669 MOZ_ASSERT(args[0].isNumber());
672 if (!args.isConstructing()) {
673 if (args.length() > 0) {
674 args.rval().set(args[0]);
675 } else {
676 args.rval().setInt32(0);
678 return true;
681 RootedObject proto(cx);
682 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Number, &proto)) {
683 return false;
686 double d = args.length() > 0 ? args[0].toNumber() : 0;
687 JSObject* obj = NumberObject::create(cx, d, proto);
688 if (!obj) {
689 return false;
691 args.rval().setObject(*obj);
692 return true;
695 // ES2020 draft rev e08b018785606bc6465a0456a79604b149007932
696 // 20.1.3 Properties of the Number Prototype Object, thisNumberValue.
697 MOZ_ALWAYS_INLINE
698 static bool ThisNumberValue(JSContext* cx, const CallArgs& args,
699 const char* methodName, double* number) {
700 HandleValue thisv = args.thisv();
702 // Step 1.
703 if (thisv.isNumber()) {
704 *number = thisv.toNumber();
705 return true;
708 // Steps 2-3.
709 auto* obj = UnwrapAndTypeCheckThis<NumberObject>(cx, args, methodName);
710 if (!obj) {
711 return false;
714 *number = obj->unbox();
715 return true;
718 // On-off helper function for the self-hosted Number_toLocaleString method.
719 // This only exists to produce an error message with the right method name.
720 bool js::ThisNumberValueForToLocaleString(JSContext* cx, unsigned argc,
721 Value* vp) {
722 CallArgs args = CallArgsFromVp(argc, vp);
724 double d;
725 if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
726 return false;
729 args.rval().setNumber(d);
730 return true;
733 static bool num_toSource(JSContext* cx, unsigned argc, Value* vp) {
734 CallArgs args = CallArgsFromVp(argc, vp);
736 double d;
737 if (!ThisNumberValue(cx, args, "toSource", &d)) {
738 return false;
741 JSStringBuilder sb(cx);
742 if (!sb.append("(new Number(") ||
743 !NumberValueToStringBuffer(NumberValue(d), sb) || !sb.append("))")) {
744 return false;
747 JSString* str = sb.finishString();
748 if (!str) {
749 return false;
751 args.rval().setString(str);
752 return true;
755 // Subtract one from DTOSTR_STANDARD_BUFFER_SIZE to exclude the null-character.
756 static_assert(
757 double_conversion::DoubleToStringConverter::kMaxCharsEcmaScriptShortest ==
758 DTOSTR_STANDARD_BUFFER_SIZE - 1,
759 "double_conversion and dtoa both agree how large the longest string "
760 "can be");
762 static_assert(DTOSTR_STANDARD_BUFFER_SIZE <= JS::MaximumNumberToStringLength,
763 "MaximumNumberToStringLength is large enough to hold the longest "
764 "string produced by a conversion");
766 MOZ_ALWAYS_INLINE
767 static JSLinearString* LookupDtoaCache(JSContext* cx, double d) {
768 if (Realm* realm = cx->realm()) {
769 if (JSLinearString* str = realm->dtoaCache.lookup(10, d)) {
770 return str;
774 return nullptr;
777 MOZ_ALWAYS_INLINE
778 static void CacheNumber(JSContext* cx, double d, JSLinearString* str) {
779 if (Realm* realm = cx->realm()) {
780 realm->dtoaCache.cache(10, d, str);
784 MOZ_ALWAYS_INLINE
785 static JSLinearString* LookupInt32ToString(JSContext* cx, int32_t si) {
786 if (si >= 0 && StaticStrings::hasInt(si)) {
787 return cx->staticStrings().getInt(si);
790 return LookupDtoaCache(cx, si);
793 template <typename T>
794 MOZ_ALWAYS_INLINE static T* BackfillInt32InBuffer(int32_t si, T* buffer,
795 size_t size, size_t* length) {
796 uint32_t ui = Abs(si);
797 MOZ_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
799 RangedPtr<T> end(buffer + size - 1, buffer, size);
800 *end = '\0';
801 RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
802 if (si < 0) {
803 *--start = '-';
806 *length = end - start;
807 return start.get();
810 template <AllowGC allowGC>
811 JSLinearString* js::Int32ToString(JSContext* cx, int32_t si) {
812 return js::Int32ToStringWithHeap<allowGC>(cx, si, gc::Heap::Default);
814 template JSLinearString* js::Int32ToString<CanGC>(JSContext* cx, int32_t si);
815 template JSLinearString* js::Int32ToString<NoGC>(JSContext* cx, int32_t si);
817 template <AllowGC allowGC>
818 JSLinearString* js::Int32ToStringWithHeap(JSContext* cx, int32_t si,
819 gc::Heap heap) {
820 if (JSLinearString* str = LookupInt32ToString(cx, si)) {
821 return str;
824 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
825 size_t length;
826 Latin1Char* start =
827 BackfillInt32InBuffer(si, buffer, std::size(buffer), &length);
829 mozilla::Range<const Latin1Char> chars(start, length);
830 JSInlineString* str = NewInlineString<allowGC>(cx, chars, heap);
831 if (!str) {
832 return nullptr;
834 if (si >= 0) {
835 str->maybeInitializeIndexValue(si);
838 CacheNumber(cx, si, str);
839 return str;
841 template JSLinearString* js::Int32ToStringWithHeap<CanGC>(JSContext* cx,
842 int32_t si,
843 gc::Heap heap);
844 template JSLinearString* js::Int32ToStringWithHeap<NoGC>(JSContext* cx,
845 int32_t si,
846 gc::Heap heap);
848 JSLinearString* js::Int32ToStringPure(JSContext* cx, int32_t si) {
849 AutoUnsafeCallWithABI unsafe;
850 return Int32ToString<NoGC>(cx, si);
853 JSAtom* js::Int32ToAtom(JSContext* cx, int32_t si) {
854 if (JSLinearString* str = LookupInt32ToString(cx, si)) {
855 return js::AtomizeString(cx, str);
858 char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
859 size_t length;
860 char* start = BackfillInt32InBuffer(
861 si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
863 Maybe<uint32_t> indexValue;
864 if (si >= 0) {
865 indexValue.emplace(si);
868 JSAtom* atom = Atomize(cx, start, length, indexValue);
869 if (!atom) {
870 return nullptr;
873 CacheNumber(cx, si, atom);
874 return atom;
877 frontend::TaggedParserAtomIndex js::Int32ToParserAtom(
878 FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, int32_t si) {
879 char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
880 size_t length;
881 char* start = BackfillInt32InBuffer(
882 si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
884 Maybe<uint32_t> indexValue;
885 if (si >= 0) {
886 indexValue.emplace(si);
889 return parserAtoms.internAscii(fc, start, length);
892 /* Returns a non-nullptr pointer to inside `buf`. */
893 template <typename T>
894 static char* Int32ToCStringWithBase(mozilla::Range<char> buf, T i, size_t* len,
895 int base) {
896 uint32_t u;
897 if constexpr (std::is_signed_v<T>) {
898 u = Abs(i);
899 } else {
900 u = i;
903 RangedPtr<char> cp = buf.end() - 1;
905 char* end = cp.get();
906 *cp = '\0';
908 /* Build the string from behind. */
909 switch (base) {
910 case 10:
911 cp = BackfillIndexInCharBuffer(u, cp);
912 break;
913 case 16:
914 do {
915 unsigned newu = u / 16;
916 *--cp = "0123456789abcdef"[u - newu * 16];
917 u = newu;
918 } while (u != 0);
919 break;
920 default:
921 MOZ_ASSERT(base >= 2 && base <= 36);
922 do {
923 unsigned newu = u / base;
924 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
925 u = newu;
926 } while (u != 0);
927 break;
929 if constexpr (std::is_signed_v<T>) {
930 if (i < 0) {
931 *--cp = '-';
935 *len = end - cp.get();
936 return cp.get();
939 /* Returns a non-nullptr pointer to inside `out`. */
940 template <typename T, size_t Length>
941 static char* Int32ToCStringWithBase(char (&out)[Length], T i, size_t* len,
942 int base) {
943 // The buffer needs to be large enough to hold the largest number, including
944 // the sign and the terminating null-character.
945 static_assert(std::numeric_limits<T>::digits + (2 * std::is_signed_v<T>) <
946 Length);
948 mozilla::Range<char> buf(out, Length);
949 return Int32ToCStringWithBase(buf, i, len, base);
952 /* Returns a non-nullptr pointer to inside `out`. */
953 template <typename T, size_t Base, size_t Length>
954 static char* Int32ToCString(char (&out)[Length], T i, size_t* len) {
955 // The buffer needs to be large enough to hold the largest number, including
956 // the sign and the terminating null-character.
957 if constexpr (Base == 10) {
958 static_assert(std::numeric_limits<T>::digits10 + 1 + std::is_signed_v<T> <
959 Length);
960 } else {
961 // Compute digits16 analog to std::numeric_limits::digits10, which is
962 // defined as |std::numeric_limits::digits * std::log10(2)| for integer
963 // types.
964 // Note: log16(2) is 1/4.
965 static_assert(Base == 16);
966 static_assert(((std::numeric_limits<T>::digits + std::is_signed_v<T>) / 4 +
967 std::is_signed_v<T>) < Length);
970 mozilla::Range<char> buf(out, Length);
971 return Int32ToCStringWithBase(buf, i, len, Base);
974 /* Returns a non-nullptr pointer to inside `cbuf`. */
975 template <typename T, size_t Base = 10>
976 static char* Int32ToCString(ToCStringBuf* cbuf, T i, size_t* len) {
977 return Int32ToCString<T, Base>(cbuf->sbuf, i, len);
980 /* Returns a non-nullptr pointer to inside `cbuf`. */
981 template <typename T, size_t Base = 10>
982 static char* Int32ToCString(Int32ToCStringBuf* cbuf, T i, size_t* len) {
983 return Int32ToCString<T, Base>(cbuf->sbuf, i, len);
986 template <AllowGC allowGC>
987 static JSString* NumberToStringWithBase(JSContext* cx, double d, int base);
989 static bool num_toString(JSContext* cx, unsigned argc, Value* vp) {
990 CallArgs args = CallArgsFromVp(argc, vp);
992 double d;
993 if (!ThisNumberValue(cx, args, "toString", &d)) {
994 return false;
997 int32_t base = 10;
998 if (args.hasDefined(0)) {
999 double d2;
1000 if (!ToInteger(cx, args[0], &d2)) {
1001 return false;
1004 if (d2 < 2 || d2 > 36) {
1005 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
1006 return false;
1009 base = int32_t(d2);
1011 JSString* str = NumberToStringWithBase<CanGC>(cx, d, base);
1012 if (!str) {
1013 return false;
1015 args.rval().setString(str);
1016 return true;
1019 #if !JS_HAS_INTL_API
1020 static bool num_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
1021 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype",
1022 "toLocaleString");
1023 CallArgs args = CallArgsFromVp(argc, vp);
1025 double d;
1026 if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
1027 return false;
1030 RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
1031 if (!str) {
1032 return false;
1036 * Create the string, move back to bytes to make string twiddling
1037 * a bit easier and so we can insert platform charset seperators.
1039 UniqueChars numBytes = EncodeAscii(cx, str);
1040 if (!numBytes) {
1041 return false;
1043 const char* num = numBytes.get();
1044 if (!num) {
1045 return false;
1049 * Find the first non-integer value, whether it be a letter as in
1050 * 'Infinity', a decimal point, or an 'e' from exponential notation.
1052 const char* nint = num;
1053 if (*nint == '-') {
1054 nint++;
1056 while (*nint >= '0' && *nint <= '9') {
1057 nint++;
1059 int digits = nint - num;
1060 const char* end = num + digits;
1061 if (!digits) {
1062 args.rval().setString(str);
1063 return true;
1066 JSRuntime* rt = cx->runtime();
1067 size_t thousandsLength = strlen(rt->thousandsSeparator);
1068 size_t decimalLength = strlen(rt->decimalSeparator);
1070 /* Figure out how long resulting string will be. */
1071 int buflen = strlen(num);
1072 if (*nint == '.') {
1073 buflen += decimalLength - 1; /* -1 to account for existing '.' */
1076 const char* numGrouping;
1077 const char* tmpGroup;
1078 numGrouping = tmpGroup = rt->numGrouping;
1079 int remainder = digits;
1080 if (*num == '-') {
1081 remainder--;
1084 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
1085 if (*tmpGroup >= remainder) {
1086 break;
1088 buflen += thousandsLength;
1089 remainder -= *tmpGroup;
1090 tmpGroup++;
1093 int nrepeat;
1094 if (*tmpGroup == '\0' && *numGrouping != '\0') {
1095 nrepeat = (remainder - 1) / tmpGroup[-1];
1096 buflen += thousandsLength * nrepeat;
1097 remainder -= nrepeat * tmpGroup[-1];
1098 } else {
1099 nrepeat = 0;
1101 tmpGroup--;
1103 char* buf = cx->pod_malloc<char>(buflen + 1);
1104 if (!buf) {
1105 return false;
1108 char* tmpDest = buf;
1109 const char* tmpSrc = num;
1111 while (*tmpSrc == '-' || remainder--) {
1112 MOZ_ASSERT(tmpDest - buf < buflen);
1113 *tmpDest++ = *tmpSrc++;
1115 while (tmpSrc < end) {
1116 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
1117 strcpy(tmpDest, rt->thousandsSeparator);
1118 tmpDest += thousandsLength;
1119 MOZ_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
1120 js_memcpy(tmpDest, tmpSrc, *tmpGroup);
1121 tmpDest += *tmpGroup;
1122 tmpSrc += *tmpGroup;
1123 if (--nrepeat < 0) {
1124 tmpGroup--;
1128 if (*nint == '.') {
1129 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
1130 strcpy(tmpDest, rt->decimalSeparator);
1131 tmpDest += decimalLength;
1132 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
1133 strcpy(tmpDest, nint + 1);
1134 } else {
1135 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
1136 strcpy(tmpDest, nint);
1139 if (cx->runtime()->localeCallbacks &&
1140 cx->runtime()->localeCallbacks->localeToUnicode) {
1141 Rooted<Value> v(cx, StringValue(str));
1142 bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v);
1143 if (ok) {
1144 args.rval().set(v);
1146 js_free(buf);
1147 return ok;
1150 str = NewStringCopyN<CanGC>(cx, buf, buflen);
1151 js_free(buf);
1152 if (!str) {
1153 return false;
1156 args.rval().setString(str);
1157 return true;
1159 #endif /* !JS_HAS_INTL_API */
1161 bool js::num_valueOf(JSContext* cx, unsigned argc, Value* vp) {
1162 CallArgs args = CallArgsFromVp(argc, vp);
1164 double d;
1165 if (!ThisNumberValue(cx, args, "valueOf", &d)) {
1166 return false;
1169 args.rval().setNumber(d);
1170 return true;
1173 static const unsigned MAX_PRECISION = 100;
1175 static bool ComputePrecisionInRange(JSContext* cx, int minPrecision,
1176 int maxPrecision, double prec,
1177 int* precision) {
1178 if (minPrecision <= prec && prec <= maxPrecision) {
1179 *precision = int(prec);
1180 return true;
1183 ToCStringBuf cbuf;
1184 char* numStr = NumberToCString(&cbuf, prec);
1185 MOZ_ASSERT(numStr);
1186 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE,
1187 numStr);
1188 return false;
1191 static constexpr size_t DoubleToStrResultBufSize = 128;
1193 template <typename Op>
1194 [[nodiscard]] static bool DoubleToStrResult(JSContext* cx, const CallArgs& args,
1195 Op op) {
1196 char buf[DoubleToStrResultBufSize];
1198 const auto& converter =
1199 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1200 double_conversion::StringBuilder builder(buf, sizeof(buf));
1202 bool ok = op(converter, builder);
1203 MOZ_RELEASE_ASSERT(ok);
1205 size_t numStrLen = builder.position();
1206 const char* numStr = builder.Finalize();
1207 MOZ_ASSERT(numStr == buf);
1208 MOZ_ASSERT(numStrLen == strlen(numStr));
1210 JSString* str = NewStringCopyN<CanGC>(cx, numStr, numStrLen);
1211 if (!str) {
1212 return false;
1215 args.rval().setString(str);
1216 return true;
1219 // ES 2021 draft 21.1.3.3.
1220 static bool num_toFixed(JSContext* cx, unsigned argc, Value* vp) {
1221 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype", "toFixed");
1222 CallArgs args = CallArgsFromVp(argc, vp);
1224 // Step 1.
1225 double d;
1226 if (!ThisNumberValue(cx, args, "toFixed", &d)) {
1227 return false;
1230 // Steps 2-5.
1231 int precision;
1232 if (args.length() == 0) {
1233 precision = 0;
1234 } else {
1235 double prec = 0;
1236 if (!ToInteger(cx, args[0], &prec)) {
1237 return false;
1240 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
1241 return false;
1245 // Step 6.
1246 if (std::isnan(d)) {
1247 args.rval().setString(cx->names().NaN);
1248 return true;
1250 if (std::isinf(d)) {
1251 if (d > 0) {
1252 args.rval().setString(cx->names().Infinity);
1253 return true;
1256 args.rval().setString(cx->names().NegativeInfinity_);
1257 return true;
1260 // Steps 7-10 for very large numbers.
1261 if (d <= -1e21 || d >= 1e+21) {
1262 JSString* s = NumberToString<CanGC>(cx, d);
1263 if (!s) {
1264 return false;
1267 args.rval().setString(s);
1268 return true;
1271 // Steps 7-12.
1273 // DoubleToStringConverter::ToFixed is documented as requiring a buffer size
1274 // of:
1276 // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint + 1
1277 // (one additional character for the sign, one for the decimal point,
1278 // and one for the null terminator)
1280 // We already ensured there are at most 21 digits before the point, and
1281 // MAX_PRECISION digits after the point.
1282 static_assert(1 + 21 + 1 + MAX_PRECISION + 1 <= DoubleToStrResultBufSize);
1284 // The double-conversion library by default has a kMaxFixedDigitsAfterPoint of
1285 // 60. Assert our modified version supports at least MAX_PRECISION (100).
1286 using DToSConverter = double_conversion::DoubleToStringConverter;
1287 static_assert(DToSConverter::kMaxFixedDigitsAfterPoint >= MAX_PRECISION);
1289 return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
1290 return converter.ToFixed(d, precision, &builder);
1294 // ES 2021 draft 21.1.3.2.
1295 static bool num_toExponential(JSContext* cx, unsigned argc, Value* vp) {
1296 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype",
1297 "toExponential");
1298 CallArgs args = CallArgsFromVp(argc, vp);
1300 // Step 1.
1301 double d;
1302 if (!ThisNumberValue(cx, args, "toExponential", &d)) {
1303 return false;
1306 // Step 2.
1307 double prec = 0;
1308 if (args.hasDefined(0)) {
1309 if (!ToInteger(cx, args[0], &prec)) {
1310 return false;
1314 // Step 3.
1315 MOZ_ASSERT_IF(!args.hasDefined(0), prec == 0);
1317 // Step 4.
1318 if (std::isnan(d)) {
1319 args.rval().setString(cx->names().NaN);
1320 return true;
1322 if (std::isinf(d)) {
1323 if (d > 0) {
1324 args.rval().setString(cx->names().Infinity);
1325 return true;
1328 args.rval().setString(cx->names().NegativeInfinity_);
1329 return true;
1332 // Step 5.
1333 int precision = 0;
1334 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
1335 return false;
1338 // Steps 6-15.
1340 // DoubleToStringConverter::ToExponential is documented as adding at most 8
1341 // characters on top of the requested digits: "the sign, the digit before the
1342 // decimal point, the decimal point, the exponent character, the exponent's
1343 // sign, and at most 3 exponent digits". In addition, the buffer must be able
1344 // to hold the trailing '\0' character.
1345 static_assert(MAX_PRECISION + 8 + 1 <= DoubleToStrResultBufSize);
1347 return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
1348 int requestedDigits = args.hasDefined(0) ? precision : -1;
1349 return converter.ToExponential(d, requestedDigits, &builder);
1353 // ES 2021 draft 21.1.3.5.
1354 static bool num_toPrecision(JSContext* cx, unsigned argc, Value* vp) {
1355 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype", "toPrecision");
1356 CallArgs args = CallArgsFromVp(argc, vp);
1358 // Step 1.
1359 double d;
1360 if (!ThisNumberValue(cx, args, "toPrecision", &d)) {
1361 return false;
1364 // Step 2.
1365 if (!args.hasDefined(0)) {
1366 JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10);
1367 if (!str) {
1368 return false;
1370 args.rval().setString(str);
1371 return true;
1374 // Step 3.
1375 double prec = 0;
1376 if (!ToInteger(cx, args[0], &prec)) {
1377 return false;
1380 // Step 4.
1381 if (std::isnan(d)) {
1382 args.rval().setString(cx->names().NaN);
1383 return true;
1385 if (std::isinf(d)) {
1386 if (d > 0) {
1387 args.rval().setString(cx->names().Infinity);
1388 return true;
1391 args.rval().setString(cx->names().NegativeInfinity_);
1392 return true;
1395 // Step 5.
1396 int precision = 0;
1397 if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, prec, &precision)) {
1398 return false;
1401 // Steps 6-14.
1403 // DoubleToStringConverter::ToPrecision is documented as adding at most 7
1404 // characters on top of the requested digits: "the sign, the decimal point,
1405 // the exponent character, the exponent's sign, and at most 3 exponent
1406 // digits". In addition, the buffer must be able to hold the trailing '\0'
1407 // character.
1408 static_assert(MAX_PRECISION + 7 + 1 <= DoubleToStrResultBufSize);
1410 return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
1411 return converter.ToPrecision(d, precision, &builder);
1415 static const JSFunctionSpec number_methods[] = {
1416 JS_FN("toSource", num_toSource, 0, 0),
1417 JS_INLINABLE_FN("toString", num_toString, 1, 0, NumberToString),
1418 #if JS_HAS_INTL_API
1419 JS_SELF_HOSTED_FN("toLocaleString", "Number_toLocaleString", 0, 0),
1420 #else
1421 JS_FN("toLocaleString", num_toLocaleString, 0, 0),
1422 #endif
1423 JS_FN("valueOf", num_valueOf, 0, 0),
1424 JS_FN("toFixed", num_toFixed, 1, 0),
1425 JS_FN("toExponential", num_toExponential, 1, 0),
1426 JS_FN("toPrecision", num_toPrecision, 1, 0),
1427 JS_FS_END};
1429 bool js::IsInteger(double d) {
1430 return std::isfinite(d) && JS::ToInteger(d) == d;
1433 static const JSFunctionSpec number_static_methods[] = {
1434 JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1, 0),
1435 JS_SELF_HOSTED_FN("isInteger", "Number_isInteger", 1, 0),
1436 JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1, 0),
1437 JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1, 0),
1438 JS_FS_END};
1440 static const JSPropertySpec number_static_properties[] = {
1441 JS_DOUBLE_PS("POSITIVE_INFINITY", mozilla::PositiveInfinity<double>(),
1442 JSPROP_READONLY | JSPROP_PERMANENT),
1443 JS_DOUBLE_PS("NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>(),
1444 JSPROP_READONLY | JSPROP_PERMANENT),
1445 JS_DOUBLE_PS("MAX_VALUE", 1.7976931348623157E+308,
1446 JSPROP_READONLY | JSPROP_PERMANENT),
1447 JS_DOUBLE_PS("MIN_VALUE", MinNumberValue<double>(),
1448 JSPROP_READONLY | JSPROP_PERMANENT),
1449 /* ES6 (April 2014 draft) 20.1.2.6 */
1450 JS_DOUBLE_PS("MAX_SAFE_INTEGER", 9007199254740991,
1451 JSPROP_READONLY | JSPROP_PERMANENT),
1452 /* ES6 (April 2014 draft) 20.1.2.10 */
1453 JS_DOUBLE_PS("MIN_SAFE_INTEGER", -9007199254740991,
1454 JSPROP_READONLY | JSPROP_PERMANENT),
1455 /* ES6 (May 2013 draft) 15.7.3.7 */
1456 JS_DOUBLE_PS("EPSILON", 2.2204460492503130808472633361816e-16,
1457 JSPROP_READONLY | JSPROP_PERMANENT),
1458 JS_PS_END};
1460 bool js::InitRuntimeNumberState(JSRuntime* rt) {
1461 // XXX If JS_HAS_INTL_API becomes true all the time at some point,
1462 // js::InitRuntimeNumberState is no longer fallible, and we should
1463 // change its return type.
1464 #if !JS_HAS_INTL_API
1465 /* Copy locale-specific separators into the runtime strings. */
1466 const char* thousandsSeparator;
1467 const char* decimalPoint;
1468 const char* grouping;
1469 # ifdef HAVE_LOCALECONV
1470 struct lconv* locale = localeconv();
1471 thousandsSeparator = locale->thousands_sep;
1472 decimalPoint = locale->decimal_point;
1473 grouping = locale->grouping;
1474 # else
1475 thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
1476 decimalPoint = getenv("LOCALE_DECIMAL_POINT");
1477 grouping = getenv("LOCALE_GROUPING");
1478 # endif
1479 if (!thousandsSeparator) {
1480 thousandsSeparator = "'";
1482 if (!decimalPoint) {
1483 decimalPoint = ".";
1485 if (!grouping) {
1486 grouping = "\3\0";
1490 * We use single malloc to get the memory for all separator and grouping
1491 * strings.
1493 size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
1494 size_t decimalPointSize = strlen(decimalPoint) + 1;
1495 size_t groupingSize = strlen(grouping) + 1;
1497 char* storage = js_pod_malloc<char>(thousandsSeparatorSize +
1498 decimalPointSize + groupingSize);
1499 if (!storage) {
1500 return false;
1503 js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
1504 rt->thousandsSeparator = storage;
1505 storage += thousandsSeparatorSize;
1507 js_memcpy(storage, decimalPoint, decimalPointSize);
1508 rt->decimalSeparator = storage;
1509 storage += decimalPointSize;
1511 js_memcpy(storage, grouping, groupingSize);
1512 rt->numGrouping = grouping;
1513 #endif /* !JS_HAS_INTL_API */
1514 return true;
1517 void js::FinishRuntimeNumberState(JSRuntime* rt) {
1518 #if !JS_HAS_INTL_API
1520 * The free also releases the memory for decimalSeparator and numGrouping
1521 * strings.
1523 char* storage = const_cast<char*>(rt->thousandsSeparator.ref());
1524 js_free(storage);
1525 #endif // !JS_HAS_INTL_API
1528 JSObject* NumberObject::createPrototype(JSContext* cx, JSProtoKey key) {
1529 NumberObject* numberProto =
1530 GlobalObject::createBlankPrototype<NumberObject>(cx, cx->global());
1531 if (!numberProto) {
1532 return nullptr;
1534 numberProto->setPrimitiveValue(0);
1535 return numberProto;
1538 static bool NumberClassFinish(JSContext* cx, HandleObject ctor,
1539 HandleObject proto) {
1540 Handle<GlobalObject*> global = cx->global();
1542 if (!JS_DefineFunctions(cx, global, number_functions)) {
1543 return false;
1546 // Number.parseInt should be the same function object as global parseInt.
1547 RootedId parseIntId(cx, NameToId(cx->names().parseInt));
1548 JSFunction* parseInt =
1549 DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING);
1550 if (!parseInt) {
1551 return false;
1553 parseInt->setJitInfo(&jit::JitInfo_NumberParseInt);
1555 RootedValue parseIntValue(cx, ObjectValue(*parseInt));
1556 if (!DefineDataProperty(cx, ctor, parseIntId, parseIntValue, 0)) {
1557 return false;
1560 // Number.parseFloat should be the same function object as global
1561 // parseFloat.
1562 RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
1563 JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
1564 num_parseFloat, 1, JSPROP_RESOLVING);
1565 if (!parseFloat) {
1566 return false;
1568 RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
1569 if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) {
1570 return false;
1573 RootedValue valueNaN(cx, JS::NaNValue());
1574 RootedValue valueInfinity(cx, JS::InfinityValue());
1576 if (!DefineDataProperty(
1577 cx, ctor, cx->names().NaN, valueNaN,
1578 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
1579 return false;
1582 // ES5 15.1.1.1, 15.1.1.2
1583 if (!NativeDefineDataProperty(
1584 cx, global, cx->names().NaN, valueNaN,
1585 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) ||
1586 !NativeDefineDataProperty(
1587 cx, global, cx->names().Infinity, valueInfinity,
1588 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
1589 return false;
1592 return true;
1595 const ClassSpec NumberObject::classSpec_ = {
1596 GenericCreateConstructor<Number, 1, gc::AllocKind::FUNCTION,
1597 &jit::JitInfo_Number>,
1598 NumberObject::createPrototype,
1599 number_static_methods,
1600 number_static_properties,
1601 number_methods,
1602 nullptr,
1603 NumberClassFinish};
1605 static char* FracNumberToCString(ToCStringBuf* cbuf, double d, size_t* len) {
1606 #ifdef DEBUG
1608 int32_t _;
1609 MOZ_ASSERT(!NumberEqualsInt32(d, &_));
1611 #endif
1614 * This is V8's implementation of the algorithm described in the
1615 * following paper:
1617 * Printing floating-point numbers quickly and accurately with integers.
1618 * Florian Loitsch, PLDI 2010.
1620 const double_conversion::DoubleToStringConverter& converter =
1621 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1622 double_conversion::StringBuilder builder(cbuf->sbuf, std::size(cbuf->sbuf));
1623 converter.ToShortest(d, &builder);
1625 *len = builder.position();
1626 return builder.Finalize();
1629 void JS::NumberToString(double d, char (&out)[MaximumNumberToStringLength]) {
1630 int32_t i;
1631 if (NumberEqualsInt32(d, &i)) {
1632 Int32ToCStringBuf cbuf;
1633 size_t len;
1634 char* loc = ::Int32ToCString(&cbuf, i, &len);
1635 memmove(out, loc, len);
1636 out[len] = '\0';
1637 } else {
1638 const double_conversion::DoubleToStringConverter& converter =
1639 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1641 double_conversion::StringBuilder builder(out, sizeof(out));
1642 converter.ToShortest(d, &builder);
1644 #ifdef DEBUG
1645 char* result =
1646 #endif
1647 builder.Finalize();
1648 MOZ_ASSERT(out == result);
1652 char* js::NumberToCString(ToCStringBuf* cbuf, double d, size_t* length) {
1653 int32_t i;
1654 size_t len;
1655 char* s = NumberEqualsInt32(d, &i) ? ::Int32ToCString(cbuf, i, &len)
1656 : FracNumberToCString(cbuf, d, &len);
1657 MOZ_ASSERT(s);
1658 if (length) {
1659 *length = len;
1661 return s;
1664 char* js::Int32ToCString(Int32ToCStringBuf* cbuf, int32_t value,
1665 size_t* length) {
1666 size_t len;
1667 char* s = ::Int32ToCString(cbuf, value, &len);
1668 MOZ_ASSERT(s);
1669 if (length) {
1670 *length = len;
1672 return s;
1675 char* js::Uint32ToCString(Int32ToCStringBuf* cbuf, uint32_t value,
1676 size_t* length) {
1677 size_t len;
1678 char* s = ::Int32ToCString(cbuf, value, &len);
1679 MOZ_ASSERT(s);
1680 if (length) {
1681 *length = len;
1683 return s;
1686 char* js::Uint32ToHexCString(Int32ToCStringBuf* cbuf, uint32_t value,
1687 size_t* length) {
1688 size_t len;
1689 char* s = ::Int32ToCString<uint32_t, 16>(cbuf, value, &len);
1690 MOZ_ASSERT(s);
1691 if (length) {
1692 *length = len;
1694 return s;
1697 template <AllowGC allowGC>
1698 static JSString* NumberToStringWithBase(JSContext* cx, double d, int base) {
1699 MOZ_ASSERT(2 <= base && base <= 36);
1701 Realm* realm = cx->realm();
1703 int32_t i;
1704 if (NumberEqualsInt32(d, &i)) {
1705 bool isBase10Int = (base == 10);
1706 if (isBase10Int) {
1707 static_assert(StaticStrings::INT_STATIC_LIMIT > 10 * 10);
1708 if (StaticStrings::hasInt(i)) {
1709 return cx->staticStrings().getInt(i);
1711 } else if (unsigned(i) < unsigned(base)) {
1712 if (i < 10) {
1713 return cx->staticStrings().getInt(i);
1715 char16_t c = 'a' + i - 10;
1716 MOZ_ASSERT(StaticStrings::hasUnit(c));
1717 return cx->staticStrings().getUnit(c);
1718 } else if (unsigned(i) < unsigned(base * base)) {
1719 static constexpr char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1720 char chars[] = {digits[i / base], digits[i % base]};
1721 JSString* str = cx->staticStrings().lookup(chars, 2);
1722 MOZ_ASSERT(str);
1723 return str;
1726 if (JSLinearString* str = realm->dtoaCache.lookup(base, d)) {
1727 return str;
1730 // Plus three to include the largest number, the sign, and the terminating
1731 // null character.
1732 constexpr size_t MaximumLength = std::numeric_limits<int32_t>::digits + 3;
1734 char buf[MaximumLength] = {};
1735 size_t numStrLen;
1736 char* numStr = Int32ToCStringWithBase(buf, i, &numStrLen, base);
1737 MOZ_ASSERT(numStrLen == strlen(numStr));
1739 JSLinearString* s = NewStringCopyN<allowGC>(cx, numStr, numStrLen);
1740 if (!s) {
1741 return nullptr;
1744 if (isBase10Int && i >= 0) {
1745 s->maybeInitializeIndexValue(i);
1748 realm->dtoaCache.cache(base, d, s);
1749 return s;
1752 if (JSLinearString* str = realm->dtoaCache.lookup(base, d)) {
1753 return str;
1756 JSLinearString* s;
1757 if (base == 10) {
1758 // We use a faster algorithm for base 10.
1759 ToCStringBuf cbuf;
1760 size_t numStrLen;
1761 char* numStr = FracNumberToCString(&cbuf, d, &numStrLen);
1762 MOZ_ASSERT(numStr);
1763 MOZ_ASSERT(numStrLen == strlen(numStr));
1765 s = NewStringCopyN<allowGC>(cx, numStr, numStrLen);
1766 if (!s) {
1767 return nullptr;
1769 } else {
1770 if (!EnsureDtoaState(cx)) {
1771 if constexpr (allowGC) {
1772 ReportOutOfMemory(cx);
1774 return nullptr;
1777 UniqueChars numStr(js_dtobasestr(cx->dtoaState, base, d));
1778 if (!numStr) {
1779 if constexpr (allowGC) {
1780 ReportOutOfMemory(cx);
1782 return nullptr;
1785 s = NewStringCopyZ<allowGC>(cx, numStr.get());
1786 if (!s) {
1787 return nullptr;
1791 realm->dtoaCache.cache(base, d, s);
1792 return s;
1795 template <AllowGC allowGC>
1796 JSString* js::NumberToString(JSContext* cx, double d) {
1797 return NumberToStringWithBase<allowGC>(cx, d, 10);
1800 template JSString* js::NumberToString<CanGC>(JSContext* cx, double d);
1802 template JSString* js::NumberToString<NoGC>(JSContext* cx, double d);
1804 JSString* js::NumberToStringPure(JSContext* cx, double d) {
1805 AutoUnsafeCallWithABI unsafe;
1806 return NumberToString<NoGC>(cx, d);
1809 JSAtom* js::NumberToAtom(JSContext* cx, double d) {
1810 int32_t si;
1811 if (NumberEqualsInt32(d, &si)) {
1812 return Int32ToAtom(cx, si);
1815 if (JSLinearString* str = LookupDtoaCache(cx, d)) {
1816 return AtomizeString(cx, str);
1819 ToCStringBuf cbuf;
1820 size_t length;
1821 char* numStr = FracNumberToCString(&cbuf, d, &length);
1822 MOZ_ASSERT(numStr);
1823 MOZ_ASSERT(std::begin(cbuf.sbuf) <= numStr && numStr < std::end(cbuf.sbuf));
1824 MOZ_ASSERT(length == strlen(numStr));
1826 JSAtom* atom = Atomize(cx, numStr, length);
1827 if (!atom) {
1828 return nullptr;
1831 CacheNumber(cx, d, atom);
1833 return atom;
1836 frontend::TaggedParserAtomIndex js::NumberToParserAtom(
1837 FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, double d) {
1838 int32_t si;
1839 if (NumberEqualsInt32(d, &si)) {
1840 return Int32ToParserAtom(fc, parserAtoms, si);
1843 ToCStringBuf cbuf;
1844 size_t length;
1845 char* numStr = FracNumberToCString(&cbuf, d, &length);
1846 MOZ_ASSERT(numStr);
1847 MOZ_ASSERT(std::begin(cbuf.sbuf) <= numStr && numStr < std::end(cbuf.sbuf));
1848 MOZ_ASSERT(length == strlen(numStr));
1850 return parserAtoms.internAscii(fc, numStr, length);
1853 JSLinearString* js::IndexToString(JSContext* cx, uint32_t index) {
1854 if (StaticStrings::hasUint(index)) {
1855 return cx->staticStrings().getUint(index);
1858 Realm* realm = cx->realm();
1859 if (JSLinearString* str = realm->dtoaCache.lookup(10, index)) {
1860 return str;
1863 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
1864 RangedPtr<Latin1Char> end(buffer + JSFatInlineString::MAX_LENGTH_LATIN1,
1865 buffer, JSFatInlineString::MAX_LENGTH_LATIN1 + 1);
1866 *end = '\0';
1867 RangedPtr<Latin1Char> start = BackfillIndexInCharBuffer(index, end);
1869 mozilla::Range<const Latin1Char> chars(start.get(), end - start);
1870 JSInlineString* str =
1871 NewInlineString<CanGC>(cx, chars, js::gc::Heap::Default);
1872 if (!str) {
1873 return nullptr;
1876 realm->dtoaCache.cache(10, index, str);
1877 return str;
1880 JSString* js::Int32ToStringWithBase(JSContext* cx, int32_t i, int32_t base,
1881 bool lowerCase) {
1882 Rooted<JSString*> str(cx, NumberToStringWithBase<CanGC>(cx, double(i), base));
1883 if (!str) {
1884 return nullptr;
1886 if (lowerCase) {
1887 return str;
1889 return StringToUpperCase(cx, str);
1892 bool js::NumberValueToStringBuffer(const Value& v, StringBuffer& sb) {
1893 /* Convert to C-string. */
1894 ToCStringBuf cbuf;
1895 const char* cstr;
1896 size_t cstrlen;
1897 if (v.isInt32()) {
1898 cstr = ::Int32ToCString(&cbuf, v.toInt32(), &cstrlen);
1899 } else {
1900 cstr = NumberToCString(&cbuf, v.toDouble(), &cstrlen);
1902 MOZ_ASSERT(cstr);
1903 MOZ_ASSERT(cstrlen == strlen(cstr));
1905 MOZ_ASSERT(cstrlen < std::size(cbuf.sbuf));
1906 return sb.append(cstr, cstrlen);
1909 template <typename CharT>
1910 inline double CharToNumber(CharT c) {
1911 if ('0' <= c && c <= '9') {
1912 return c - '0';
1914 if (unicode::IsSpace(c)) {
1915 return 0.0;
1917 return GenericNaN();
1920 template <typename CharT>
1921 inline bool CharsToNonDecimalNumber(const CharT* start, const CharT* end,
1922 double* result) {
1923 MOZ_ASSERT(end - start >= 2);
1924 MOZ_ASSERT(start[0] == '0');
1926 int radix = 0;
1927 if (start[1] == 'b' || start[1] == 'B') {
1928 radix = 2;
1929 } else if (start[1] == 'o' || start[1] == 'O') {
1930 radix = 8;
1931 } else if (start[1] == 'x' || start[1] == 'X') {
1932 radix = 16;
1933 } else {
1934 return false;
1937 // It's probably a non-decimal number. Accept if there's at least one digit
1938 // after the 0b|0o|0x, and if no non-whitespace characters follow all the
1939 // digits.
1940 const CharT* endptr;
1941 double d;
1942 MOZ_ALWAYS_TRUE(GetPrefixIntegerImpl(
1943 start + 2, end, radix, IntegerSeparatorHandling::None, &endptr, &d));
1944 if (endptr == start + 2 || SkipSpace(endptr, end) != end) {
1945 *result = GenericNaN();
1946 } else {
1947 *result = d;
1949 return true;
1952 template <typename CharT>
1953 double js::CharsToNumber(const CharT* chars, size_t length) {
1954 if (length == 1) {
1955 return CharToNumber(chars[0]);
1958 const CharT* end = chars + length;
1959 const CharT* start = SkipSpace(chars, end);
1961 // ECMA doesn't allow signed non-decimal numbers (bug 273467).
1962 if (end - start >= 2 && start[0] == '0') {
1963 double d;
1964 if (CharsToNonDecimalNumber(start, end, &d)) {
1965 return d;
1970 * Note that ECMA doesn't treat a string beginning with a '0' as
1971 * an octal number here. This works because all such numbers will
1972 * be interpreted as decimal by js_strtod. Also, any hex numbers
1973 * that have made it here (which can only be negative ones) will
1974 * be treated as 0 without consuming the 'x' by js_strtod.
1976 const CharT* ep;
1977 double d = js_strtod(start, end, &ep);
1978 if (SkipSpace(ep, end) != end) {
1979 return GenericNaN();
1981 return d;
1984 template double js::CharsToNumber(const Latin1Char* chars, size_t length);
1986 template double js::CharsToNumber(const char16_t* chars, size_t length);
1988 double js::LinearStringToNumber(JSLinearString* str) {
1989 if (str->hasIndexValue()) {
1990 return str->getIndexValue();
1993 AutoCheckCannotGC nogc;
1994 return str->hasLatin1Chars()
1995 ? CharsToNumber(str->latin1Chars(nogc), str->length())
1996 : CharsToNumber(str->twoByteChars(nogc), str->length());
1999 bool js::StringToNumber(JSContext* cx, JSString* str, double* result) {
2000 JSLinearString* linearStr = str->ensureLinear(cx);
2001 if (!linearStr) {
2002 return false;
2005 *result = LinearStringToNumber(linearStr);
2006 return true;
2009 bool js::StringToNumberPure(JSContext* cx, JSString* str, double* result) {
2010 // IC Code calls this directly.
2011 AutoUnsafeCallWithABI unsafe;
2013 if (!StringToNumber(cx, str, result)) {
2014 cx->recoverFromOutOfMemory();
2015 return false;
2017 return true;
2020 JS_PUBLIC_API bool js::ToNumberSlow(JSContext* cx, HandleValue v_,
2021 double* out) {
2022 RootedValue v(cx, v_);
2023 MOZ_ASSERT(!v.isNumber());
2025 if (!v.isPrimitive()) {
2026 if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) {
2027 return false;
2030 if (v.isNumber()) {
2031 *out = v.toNumber();
2032 return true;
2035 if (v.isString()) {
2036 return StringToNumber(cx, v.toString(), out);
2038 if (v.isBoolean()) {
2039 *out = v.toBoolean() ? 1.0 : 0.0;
2040 return true;
2042 if (v.isNull()) {
2043 *out = 0.0;
2044 return true;
2046 if (v.isUndefined()) {
2047 *out = GenericNaN();
2048 return true;
2050 #ifdef ENABLE_RECORD_TUPLE
2051 if (v.isExtendedPrimitive()) {
2052 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2053 JSMSG_RECORD_TUPLE_TO_NUMBER);
2054 return false;
2056 #endif
2058 MOZ_ASSERT(v.isSymbol() || v.isBigInt());
2059 unsigned errnum = JSMSG_SYMBOL_TO_NUMBER;
2060 if (v.isBigInt()) {
2061 errnum = JSMSG_BIGINT_TO_NUMBER;
2063 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errnum);
2064 return false;
2067 // BigInt proposal section 3.1.6
2068 bool js::ToNumericSlow(JSContext* cx, MutableHandleValue vp) {
2069 MOZ_ASSERT(!vp.isNumeric());
2071 // Step 1.
2072 if (!vp.isPrimitive()) {
2073 if (!ToPrimitive(cx, JSTYPE_NUMBER, vp)) {
2074 return false;
2078 // Step 2.
2079 if (vp.isBigInt()) {
2080 return true;
2083 // Step 3.
2084 return ToNumber(cx, vp);
2088 * Convert a value to an int8_t, according to the WebIDL rules for byte
2089 * conversion. Return converted value in *out on success, false on failure.
2091 JS_PUBLIC_API bool js::ToInt8Slow(JSContext* cx, const HandleValue v,
2092 int8_t* out) {
2093 MOZ_ASSERT(!v.isInt32());
2094 double d;
2095 if (v.isDouble()) {
2096 d = v.toDouble();
2097 } else {
2098 if (!ToNumberSlow(cx, v, &d)) {
2099 return false;
2102 *out = ToInt8(d);
2103 return true;
2107 * Convert a value to an uint8_t, according to the ToUInt8() function in ES6
2108 * ECMA-262, 7.1.10. Return converted value in *out on success, false on
2109 * failure.
2111 JS_PUBLIC_API bool js::ToUint8Slow(JSContext* cx, const HandleValue v,
2112 uint8_t* out) {
2113 MOZ_ASSERT(!v.isInt32());
2114 double d;
2115 if (v.isDouble()) {
2116 d = v.toDouble();
2117 } else {
2118 if (!ToNumberSlow(cx, v, &d)) {
2119 return false;
2122 *out = ToUint8(d);
2123 return true;
2127 * Convert a value to an int16_t, according to the WebIDL rules for short
2128 * conversion. Return converted value in *out on success, false on failure.
2130 JS_PUBLIC_API bool js::ToInt16Slow(JSContext* cx, const HandleValue v,
2131 int16_t* out) {
2132 MOZ_ASSERT(!v.isInt32());
2133 double d;
2134 if (v.isDouble()) {
2135 d = v.toDouble();
2136 } else {
2137 if (!ToNumberSlow(cx, v, &d)) {
2138 return false;
2141 *out = ToInt16(d);
2142 return true;
2146 * Convert a value to an int64_t, according to the WebIDL rules for long long
2147 * conversion. Return converted value in *out on success, false on failure.
2149 JS_PUBLIC_API bool js::ToInt64Slow(JSContext* cx, const HandleValue v,
2150 int64_t* out) {
2151 MOZ_ASSERT(!v.isInt32());
2152 double d;
2153 if (v.isDouble()) {
2154 d = v.toDouble();
2155 } else {
2156 if (!ToNumberSlow(cx, v, &d)) {
2157 return false;
2160 *out = ToInt64(d);
2161 return true;
2165 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned
2166 * long long conversion. Return converted value in *out on success, false on
2167 * failure.
2169 JS_PUBLIC_API bool js::ToUint64Slow(JSContext* cx, const HandleValue v,
2170 uint64_t* out) {
2171 MOZ_ASSERT(!v.isInt32());
2172 double d;
2173 if (v.isDouble()) {
2174 d = v.toDouble();
2175 } else {
2176 if (!ToNumberSlow(cx, v, &d)) {
2177 return false;
2180 *out = ToUint64(d);
2181 return true;
2184 JS_PUBLIC_API bool js::ToInt32Slow(JSContext* cx, const HandleValue v,
2185 int32_t* out) {
2186 MOZ_ASSERT(!v.isInt32());
2187 double d;
2188 if (v.isDouble()) {
2189 d = v.toDouble();
2190 } else {
2191 if (!ToNumberSlow(cx, v, &d)) {
2192 return false;
2195 *out = ToInt32(d);
2196 return true;
2199 bool js::ToInt32OrBigIntSlow(JSContext* cx, MutableHandleValue vp) {
2200 MOZ_ASSERT(!vp.isInt32());
2201 if (vp.isDouble()) {
2202 vp.setInt32(ToInt32(vp.toDouble()));
2203 return true;
2206 if (!ToNumeric(cx, vp)) {
2207 return false;
2210 if (vp.isBigInt()) {
2211 return true;
2214 vp.setInt32(ToInt32(vp.toNumber()));
2215 return true;
2218 JS_PUBLIC_API bool js::ToUint32Slow(JSContext* cx, const HandleValue v,
2219 uint32_t* out) {
2220 MOZ_ASSERT(!v.isInt32());
2221 double d;
2222 if (v.isDouble()) {
2223 d = v.toDouble();
2224 } else {
2225 if (!ToNumberSlow(cx, v, &d)) {
2226 return false;
2229 *out = ToUint32(d);
2230 return true;
2233 JS_PUBLIC_API bool js::ToUint16Slow(JSContext* cx, const HandleValue v,
2234 uint16_t* out) {
2235 MOZ_ASSERT(!v.isInt32());
2236 double d;
2237 if (v.isDouble()) {
2238 d = v.toDouble();
2239 } else if (!ToNumberSlow(cx, v, &d)) {
2240 return false;
2242 *out = ToUint16(d);
2243 return true;
2246 // ES2017 draft 7.1.17 ToIndex
2247 bool js::ToIndexSlow(JSContext* cx, JS::HandleValue v,
2248 const unsigned errorNumber, uint64_t* index) {
2249 MOZ_ASSERT_IF(v.isInt32(), v.toInt32() < 0);
2251 // Step 1.
2252 if (v.isUndefined()) {
2253 *index = 0;
2254 return true;
2257 // Step 2.a.
2258 double integerIndex;
2259 if (!ToInteger(cx, v, &integerIndex)) {
2260 return false;
2263 // Inlined version of ToLength.
2264 // 1. Already an integer.
2265 // 2. Step eliminates < 0, +0 == -0 with SameValueZero.
2266 // 3/4. Limit to <= 2^53-1, so everything above should fail.
2267 if (integerIndex < 0 || integerIndex >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
2268 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
2269 return false;
2272 // Step 3.
2273 *index = uint64_t(integerIndex);
2274 return true;
2277 template <typename CharT>
2278 double js_strtod(const CharT* begin, const CharT* end, const CharT** dEnd) {
2279 const CharT* s = SkipSpace(begin, end);
2280 size_t length = end - s;
2283 // StringToDouble can make indirect calls but can't trigger a GC.
2284 JS::AutoSuppressGCAnalysis nogc;
2286 using SToDConverter = double_conversion::StringToDoubleConverter;
2287 SToDConverter converter(SToDConverter::ALLOW_TRAILING_JUNK,
2288 /* empty_string_value = */ 0.0,
2289 /* junk_string_value = */ GenericNaN(),
2290 /* infinity_symbol = */ nullptr,
2291 /* nan_symbol = */ nullptr);
2292 int lengthInt = mozilla::AssertedCast<int>(length);
2293 double d;
2294 int processed = 0;
2295 if constexpr (std::is_same_v<CharT, char16_t>) {
2296 d = converter.StringToDouble(reinterpret_cast<const uc16*>(s), lengthInt,
2297 &processed);
2298 } else {
2299 static_assert(std::is_same_v<CharT, Latin1Char>);
2300 d = converter.StringToDouble(reinterpret_cast<const char*>(s), lengthInt,
2301 &processed);
2303 MOZ_ASSERT(processed >= 0);
2304 MOZ_ASSERT(processed <= lengthInt);
2306 if (processed > 0) {
2307 *dEnd = s + processed;
2308 return d;
2312 // Try to parse +Infinity, -Infinity or Infinity. Note that we do this here
2313 // instead of using StringToDoubleConverter's infinity_symbol because it's
2314 // faster: the code below is less generic and not on the fast path for regular
2315 // doubles.
2316 static constexpr std::string_view Infinity = "Infinity";
2317 if (length >= Infinity.length()) {
2318 const CharT* afterSign = s;
2319 bool negative = (*afterSign == '-');
2320 if (negative || *afterSign == '+') {
2321 afterSign++;
2323 MOZ_ASSERT(afterSign < end);
2324 if (*afterSign == 'I' && size_t(end - afterSign) >= Infinity.length() &&
2325 EqualChars(afterSign, Infinity.data(), Infinity.length())) {
2326 *dEnd = afterSign + Infinity.length();
2327 return negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
2331 *dEnd = begin;
2332 return 0.0;
2335 template double js_strtod(const char16_t* begin, const char16_t* end,
2336 const char16_t** dEnd);
2338 template double js_strtod(const Latin1Char* begin, const Latin1Char* end,
2339 const Latin1Char** dEnd);