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/. */
8 * JS number type and wrapper class.
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"
23 #ifdef HAVE_LOCALECONV
27 #include <string.h> // memmove
28 #include <string_view>
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_*
41 # include "js/LocaleSensitive.h"
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"
65 using mozilla::AsciiAlphanumericToNumber
;
66 using mozilla::IsAsciiAlphanumeric
;
67 using mozilla::IsAsciiDigit
;
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
;
88 static bool EnsureDtoaState(JSContext
* cx
) {
90 cx
->dtoaState
= NewDtoaState();
98 template <typename CharT
>
99 static inline void AssertWellPlacedNumericSeparator(const CharT
* s
,
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");
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 */
124 BinaryDigitReader(int base
, const CharT
* start
, const CharT
* end
)
132 /* Return the next binary digit from the number, or -1 if done. */
134 if (digitMask
== 0) {
141 AssertWellPlacedNumericSeparator(cur
- 1, start
, end
);
145 MOZ_ASSERT(IsAsciiAlphanumeric(c
));
146 digit
= AsciiAlphanumericToNumber(c
);
147 digitMask
= base
>> 1;
150 int bit
= (digit
& digitMask
) != 0;
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. */
175 bit
= bdr
.nextDigit();
178 MOZ_ASSERT(bit
== 1); // guaranteed by Get{Prefix,Decimal}Integer
180 /* Gather the 53 significant bits (including the leading 1). */
182 for (int j
= 52; j
> 0; j
--) {
183 bit
= bdr
.nextDigit();
187 value
= value
* 2 + bit
;
190 /* bit2 is the 54th bit (the first dropped from the mantissa). */
191 int bit2
= bdr
.nextDigit();
194 int sticky
= 0; /* sticky is 1 if any bit beyond the 54th is 1 */
197 while ((bit3
= bdr
.nextDigit()) >= 0) {
201 value
+= bit2
& (bit
| sticky
);
208 template <typename CharT
>
209 double js::ParseDecimalNumber(const mozilla::Range
<const CharT
> chars
) {
210 MOZ_ASSERT(chars
.length() > 0);
212 RangedPtr
<const CharT
> s
= chars
.begin(), end
= chars
.end();
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");
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
;
240 for (; s
< end
; s
++) {
242 if (!IsAsciiAlphanumeric(c
)) {
244 separatorHandling
== IntegerSeparatorHandling::SkipUnderscore
) {
245 AssertWellPlacedNumericSeparator(s
, start
, end
);
251 uint8_t digit
= AsciiAlphanumericToNumber(c
);
256 d
= d
* base
+ digit
;
262 /* If we haven't reached the limit of integer precision, we're done. */
263 if (d
< DOUBLE_INTEGRAL_PRECISION_LIMIT
) {
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.
276 if ((base
& (base
- 1)) == 0) {
277 *dp
= ComputeAccurateBinaryBaseInteger(start
, s
, base
);
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
)) {
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
);
302 template bool GetPrefixInteger(const char16_t
* start
, const char16_t
* end
,
304 IntegerSeparatorHandling separatorHandling
,
305 const char16_t
** endp
, double* dp
);
307 template bool GetPrefixInteger(const Latin1Char
* start
, const Latin1Char
* end
,
309 IntegerSeparatorHandling separatorHandling
,
310 const Latin1Char
** endp
, double* dp
);
314 template <typename CharT
>
315 bool js::GetDecimalInteger(const CharT
* start
, const CharT
* end
, double* dp
) {
316 MOZ_ASSERT(start
<= end
);
319 for (const CharT
* s
= start
; s
< end
; s
++) {
322 AssertWellPlacedNumericSeparator(s
, start
, end
);
325 MOZ_ASSERT(IsAsciiDigit(c
));
330 // If we haven't reached the limit of integer precision, we're done.
331 if (d
< DOUBLE_INTEGRAL_PRECISION_LIMIT
) {
336 // Otherwise compute the correct integer using GetDecimal.
337 return GetDecimal(start
, end
, dp
);
342 template bool GetDecimalInteger(const char16_t
* start
, const char16_t
* end
,
345 template bool GetDecimalInteger(const Latin1Char
* start
, const Latin1Char
* end
,
349 bool GetDecimalInteger
<Utf8Unit
>(const Utf8Unit
* start
, const Utf8Unit
* end
,
351 return GetDecimalInteger(Utf8AsUnsignedChars(start
), Utf8AsUnsignedChars(end
),
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
);
371 double d
= converter
.StringToDouble(chars
, lengthInt
, &processed
);
372 MOZ_ASSERT(processed
>= 0);
373 MOZ_ASSERT(size_t(processed
) == length
);
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
);
383 static_assert(std::is_same_v
<CharT
, Latin1Char
>);
384 *dp
= convert(reinterpret_cast<const char*>(start
), length
);
389 Vector
<char, 32, SystemAllocPolicy
> chars
;
390 if (!chars
.growByUninitialized(length
)) {
394 const CharT
* s
= start
;
396 for (; s
< end
; s
++) {
399 AssertWellPlacedNumericSeparator(s
, start
, end
);
402 MOZ_ASSERT(IsAsciiDigit(c
) || c
== '.' || c
== 'e' || c
== 'E' ||
403 c
== '+' || c
== '-');
404 chars
[i
++] = char(c
);
407 *dp
= convert(chars
.begin(), i
);
413 template bool GetDecimal(const char16_t
* start
, const char16_t
* end
,
416 template bool GetDecimal(const Latin1Char
* start
, const Latin1Char
* end
,
420 bool GetDecimal
<Utf8Unit
>(const Utf8Unit
* start
, const Utf8Unit
* end
,
422 return GetDecimal(Utf8AsUnsignedChars(start
), Utf8AsUnsignedChars(end
), dp
);
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();
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);
440 args
.rval().set(args
[0]);
445 JSString
* str
= ToString
<CanGC
>(cx
, args
[0]);
450 if (str
->hasIndexValue()) {
451 args
.rval().setNumber(str
->getIndexValue());
455 JSLinearString
* linear
= str
->ensureLinear(cx
);
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
);
470 const char16_t
* begin
= linear
->twoByteChars(nogc
);
472 d
= js_strtod(begin
, begin
+ linear
->length(), &end
);
478 args
.rval().setDouble(d
);
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
) {
488 const CharT
* end
= chars
+ length
;
489 const CharT
* s
= SkipSpace(chars
, end
);
491 MOZ_ASSERT(chars
<= s
);
492 MOZ_ASSERT(s
<= end
);
495 bool negative
= (s
!= end
&& s
[0] == '-');
498 if (s
!= end
&& (s
[0] == '-' || s
[0] == '+')) {
504 if (end
- s
>= 2 && s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X')) {
511 const CharT
* actualEnd
;
513 if (!js::GetPrefixInteger(s
, end
, radix
, IntegerSeparatorHandling::None
,
515 ReportOutOfMemory(cx
);
519 if (s
== actualEnd
) {
522 *res
= negative
? -d
: d
;
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
) {
532 bool stripPrefix
= true;
536 if (radix
< 2 || radix
> 36) {
547 MOZ_ASSERT(2 <= radix
&& radix
<= 36);
549 JSLinearString
* linear
= str
->ensureLinear(cx
);
555 AutoCheckCannotGC nogc
;
556 size_t length
= linear
->length();
558 if (linear
->hasLatin1Chars()) {
559 if (!ParseIntImpl(cx
, linear
->latin1Chars(nogc
), length
, stripPrefix
, radix
,
564 if (!ParseIntImpl(cx
, linear
->twoByteChars(nogc
), length
, stripPrefix
,
570 result
.setNumber(number
);
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();
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]);
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
));
610 if (-DOUBLE_DECIMAL_IN_SHORTEST_HIGH
< d
&&
611 d
<= -DOUBLE_DECIMAL_IN_SHORTEST_LOW
) {
612 args
.rval().setNumber(-floor(-d
));
616 args
.rval().setInt32(0);
621 if (args
[0].isString()) {
622 JSString
* str
= args
[0].toString();
623 if (str
->hasIndexValue()) {
624 args
.rval().setNumber(str
->getIndexValue());
631 RootedString
inputString(cx
, ToString
<CanGC
>(cx
, args
[0]));
638 if (args
.hasDefined(1)) {
639 if (!ToInt32(cx
, args
[1], &radix
)) {
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
),
653 const JSClass
NumberObject::class_
= {
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])) {
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]);
676 args
.rval().setInt32(0);
681 RootedObject
proto(cx
);
682 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_Number
, &proto
)) {
686 double d
= args
.length() > 0 ? args
[0].toNumber() : 0;
687 JSObject
* obj
= NumberObject::create(cx
, d
, proto
);
691 args
.rval().setObject(*obj
);
695 // ES2020 draft rev e08b018785606bc6465a0456a79604b149007932
696 // 20.1.3 Properties of the Number Prototype Object, thisNumberValue.
698 static bool ThisNumberValue(JSContext
* cx
, const CallArgs
& args
,
699 const char* methodName
, double* number
) {
700 HandleValue thisv
= args
.thisv();
703 if (thisv
.isNumber()) {
704 *number
= thisv
.toNumber();
709 auto* obj
= UnwrapAndTypeCheckThis
<NumberObject
>(cx
, args
, methodName
);
714 *number
= obj
->unbox();
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
,
722 CallArgs args
= CallArgsFromVp(argc
, vp
);
725 if (!ThisNumberValue(cx
, args
, "toLocaleString", &d
)) {
729 args
.rval().setNumber(d
);
733 static bool num_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
734 CallArgs args
= CallArgsFromVp(argc
, vp
);
737 if (!ThisNumberValue(cx
, args
, "toSource", &d
)) {
741 JSStringBuilder
sb(cx
);
742 if (!sb
.append("(new Number(") ||
743 !NumberValueToStringBuffer(NumberValue(d
), sb
) || !sb
.append("))")) {
747 JSString
* str
= sb
.finishString();
751 args
.rval().setString(str
);
755 // Subtract one from DTOSTR_STANDARD_BUFFER_SIZE to exclude the null-character.
757 double_conversion::DoubleToStringConverter::kMaxCharsEcmaScriptShortest
==
758 DTOSTR_STANDARD_BUFFER_SIZE
- 1,
759 "double_conversion and dtoa both agree how large the longest string "
762 static_assert(DTOSTR_STANDARD_BUFFER_SIZE
<= JS::MaximumNumberToStringLength
,
763 "MaximumNumberToStringLength is large enough to hold the longest "
764 "string produced by a conversion");
767 static JSLinearString
* LookupDtoaCache(JSContext
* cx
, double d
) {
768 if (Realm
* realm
= cx
->realm()) {
769 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(10, d
)) {
778 static void CacheNumber(JSContext
* cx
, double d
, JSLinearString
* str
) {
779 if (Realm
* realm
= cx
->realm()) {
780 realm
->dtoaCache
.cache(10, d
, str
);
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
);
801 RangedPtr
<T
> start
= BackfillIndexInCharBuffer(ui
, end
);
806 *length
= end
- start
;
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
,
820 if (JSLinearString
* str
= LookupInt32ToString(cx
, si
)) {
824 Latin1Char buffer
[JSFatInlineString::MAX_LENGTH_LATIN1
+ 1];
827 BackfillInt32InBuffer(si
, buffer
, std::size(buffer
), &length
);
829 mozilla::Range
<const Latin1Char
> chars(start
, length
);
830 JSInlineString
* str
= NewInlineString
<allowGC
>(cx
, chars
, heap
);
835 str
->maybeInitializeIndexValue(si
);
838 CacheNumber(cx
, si
, str
);
841 template JSLinearString
* js::Int32ToStringWithHeap
<CanGC
>(JSContext
* cx
,
844 template JSLinearString
* js::Int32ToStringWithHeap
<NoGC
>(JSContext
* cx
,
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];
860 char* start
= BackfillInt32InBuffer(
861 si
, buffer
, JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1, &length
);
863 Maybe
<uint32_t> indexValue
;
865 indexValue
.emplace(si
);
868 JSAtom
* atom
= Atomize(cx
, start
, length
, indexValue
);
873 CacheNumber(cx
, si
, atom
);
877 frontend::TaggedParserAtomIndex
js::Int32ToParserAtom(
878 FrontendContext
* fc
, frontend::ParserAtomsTable
& parserAtoms
, int32_t si
) {
879 char buffer
[JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1];
881 char* start
= BackfillInt32InBuffer(
882 si
, buffer
, JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1, &length
);
884 Maybe
<uint32_t> indexValue
;
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
,
897 if constexpr (std::is_signed_v
<T
>) {
903 RangedPtr
<char> cp
= buf
.end() - 1;
905 char* end
= cp
.get();
908 /* Build the string from behind. */
911 cp
= BackfillIndexInCharBuffer(u
, cp
);
915 unsigned newu
= u
/ 16;
916 *--cp
= "0123456789abcdef"[u
- newu
* 16];
921 MOZ_ASSERT(base
>= 2 && base
<= 36);
923 unsigned newu
= u
/ base
;
924 *--cp
= "0123456789abcdefghijklmnopqrstuvwxyz"[u
- newu
* base
];
929 if constexpr (std::is_signed_v
<T
>) {
935 *len
= end
- 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
,
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
>) <
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
> <
961 // Compute digits16 analog to std::numeric_limits::digits10, which is
962 // defined as |std::numeric_limits::digits * std::log10(2)| for integer
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
);
993 if (!ThisNumberValue(cx
, args
, "toString", &d
)) {
998 if (args
.hasDefined(0)) {
1000 if (!ToInteger(cx
, args
[0], &d2
)) {
1004 if (d2
< 2 || d2
> 36) {
1005 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_RADIX
);
1011 JSString
* str
= NumberToStringWithBase
<CanGC
>(cx
, d
, base
);
1015 args
.rval().setString(str
);
1019 #if !JS_HAS_INTL_API
1020 static bool num_toLocaleString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1021 AutoJSMethodProfilerEntry
pseudoFrame(cx
, "Number.prototype",
1023 CallArgs args
= CallArgsFromVp(argc
, vp
);
1026 if (!ThisNumberValue(cx
, args
, "toLocaleString", &d
)) {
1030 RootedString
str(cx
, NumberToStringWithBase
<CanGC
>(cx
, d
, 10));
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
);
1043 const char* num
= numBytes
.get();
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
;
1056 while (*nint
>= '0' && *nint
<= '9') {
1059 int digits
= nint
- num
;
1060 const char* end
= num
+ digits
;
1062 args
.rval().setString(str
);
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
);
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
;
1084 while (*tmpGroup
!= CHAR_MAX
&& *tmpGroup
!= '\0') {
1085 if (*tmpGroup
>= remainder
) {
1088 buflen
+= thousandsLength
;
1089 remainder
-= *tmpGroup
;
1094 if (*tmpGroup
== '\0' && *numGrouping
!= '\0') {
1095 nrepeat
= (remainder
- 1) / tmpGroup
[-1];
1096 buflen
+= thousandsLength
* nrepeat
;
1097 remainder
-= nrepeat
* tmpGroup
[-1];
1103 char* buf
= cx
->pod_malloc
<char>(buflen
+ 1);
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) {
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);
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
);
1150 str
= NewStringCopyN
<CanGC
>(cx
, buf
, buflen
);
1156 args
.rval().setString(str
);
1159 #endif /* !JS_HAS_INTL_API */
1161 bool js::num_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1162 CallArgs args
= CallArgsFromVp(argc
, vp
);
1165 if (!ThisNumberValue(cx
, args
, "valueOf", &d
)) {
1169 args
.rval().setNumber(d
);
1173 static const unsigned MAX_PRECISION
= 100;
1175 static bool ComputePrecisionInRange(JSContext
* cx
, int minPrecision
,
1176 int maxPrecision
, double prec
,
1178 if (minPrecision
<= prec
&& prec
<= maxPrecision
) {
1179 *precision
= int(prec
);
1184 char* numStr
= NumberToCString(&cbuf
, prec
);
1186 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_PRECISION_RANGE
,
1191 static constexpr size_t DoubleToStrResultBufSize
= 128;
1193 template <typename Op
>
1194 [[nodiscard
]] static bool DoubleToStrResult(JSContext
* cx
, const CallArgs
& args
,
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
);
1215 args
.rval().setString(str
);
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
);
1226 if (!ThisNumberValue(cx
, args
, "toFixed", &d
)) {
1232 if (args
.length() == 0) {
1236 if (!ToInteger(cx
, args
[0], &prec
)) {
1240 if (!ComputePrecisionInRange(cx
, 0, MAX_PRECISION
, prec
, &precision
)) {
1246 if (std::isnan(d
)) {
1247 args
.rval().setString(cx
->names().NaN
);
1250 if (std::isinf(d
)) {
1252 args
.rval().setString(cx
->names().Infinity
);
1256 args
.rval().setString(cx
->names().NegativeInfinity_
);
1260 // Steps 7-10 for very large numbers.
1261 if (d
<= -1e21
|| d
>= 1e+21) {
1262 JSString
* s
= NumberToString
<CanGC
>(cx
, d
);
1267 args
.rval().setString(s
);
1273 // DoubleToStringConverter::ToFixed is documented as requiring a buffer size
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",
1298 CallArgs args
= CallArgsFromVp(argc
, vp
);
1302 if (!ThisNumberValue(cx
, args
, "toExponential", &d
)) {
1308 if (args
.hasDefined(0)) {
1309 if (!ToInteger(cx
, args
[0], &prec
)) {
1315 MOZ_ASSERT_IF(!args
.hasDefined(0), prec
== 0);
1318 if (std::isnan(d
)) {
1319 args
.rval().setString(cx
->names().NaN
);
1322 if (std::isinf(d
)) {
1324 args
.rval().setString(cx
->names().Infinity
);
1328 args
.rval().setString(cx
->names().NegativeInfinity_
);
1334 if (!ComputePrecisionInRange(cx
, 0, MAX_PRECISION
, prec
, &precision
)) {
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
);
1360 if (!ThisNumberValue(cx
, args
, "toPrecision", &d
)) {
1365 if (!args
.hasDefined(0)) {
1366 JSString
* str
= NumberToStringWithBase
<CanGC
>(cx
, d
, 10);
1370 args
.rval().setString(str
);
1376 if (!ToInteger(cx
, args
[0], &prec
)) {
1381 if (std::isnan(d
)) {
1382 args
.rval().setString(cx
->names().NaN
);
1385 if (std::isinf(d
)) {
1387 args
.rval().setString(cx
->names().Infinity
);
1391 args
.rval().setString(cx
->names().NegativeInfinity_
);
1397 if (!ComputePrecisionInRange(cx
, 1, MAX_PRECISION
, prec
, &precision
)) {
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'
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
),
1419 JS_SELF_HOSTED_FN("toLocaleString", "Number_toLocaleString", 0, 0),
1421 JS_FN("toLocaleString", num_toLocaleString
, 0, 0),
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),
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),
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
),
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
;
1475 thousandsSeparator
= getenv("LOCALE_THOUSANDS_SEP");
1476 decimalPoint
= getenv("LOCALE_DECIMAL_POINT");
1477 grouping
= getenv("LOCALE_GROUPING");
1479 if (!thousandsSeparator
) {
1480 thousandsSeparator
= "'";
1482 if (!decimalPoint
) {
1490 * We use single malloc to get the memory for all separator and grouping
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
);
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 */
1517 void js::FinishRuntimeNumberState(JSRuntime
* rt
) {
1518 #if !JS_HAS_INTL_API
1520 * The free also releases the memory for decimalSeparator and numGrouping
1523 char* storage
= const_cast<char*>(rt
->thousandsSeparator
.ref());
1525 #endif // !JS_HAS_INTL_API
1528 JSObject
* NumberObject::createPrototype(JSContext
* cx
, JSProtoKey key
) {
1529 NumberObject
* numberProto
=
1530 GlobalObject::createBlankPrototype
<NumberObject
>(cx
, cx
->global());
1534 numberProto
->setPrimitiveValue(0);
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
)) {
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
);
1553 parseInt
->setJitInfo(&jit::JitInfo_NumberParseInt
);
1555 RootedValue
parseIntValue(cx
, ObjectValue(*parseInt
));
1556 if (!DefineDataProperty(cx
, ctor
, parseIntId
, parseIntValue
, 0)) {
1560 // Number.parseFloat should be the same function object as global
1562 RootedId
parseFloatId(cx
, NameToId(cx
->names().parseFloat
));
1563 JSFunction
* parseFloat
= DefineFunction(cx
, global
, parseFloatId
,
1564 num_parseFloat
, 1, JSPROP_RESOLVING
);
1568 RootedValue
parseFloatValue(cx
, ObjectValue(*parseFloat
));
1569 if (!DefineDataProperty(cx
, ctor
, parseFloatId
, parseFloatValue
, 0)) {
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
)) {
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
)) {
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
,
1605 static char* FracNumberToCString(ToCStringBuf
* cbuf
, double d
, size_t* len
) {
1609 MOZ_ASSERT(!NumberEqualsInt32(d
, &_
));
1614 * This is V8's implementation of the algorithm described in the
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
]) {
1631 if (NumberEqualsInt32(d
, &i
)) {
1632 Int32ToCStringBuf cbuf
;
1634 char* loc
= ::Int32ToCString(&cbuf
, i
, &len
);
1635 memmove(out
, loc
, len
);
1638 const double_conversion::DoubleToStringConverter
& converter
=
1639 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1641 double_conversion::StringBuilder
builder(out
, sizeof(out
));
1642 converter
.ToShortest(d
, &builder
);
1648 MOZ_ASSERT(out
== result
);
1652 char* js::NumberToCString(ToCStringBuf
* cbuf
, double d
, size_t* length
) {
1655 char* s
= NumberEqualsInt32(d
, &i
) ? ::Int32ToCString(cbuf
, i
, &len
)
1656 : FracNumberToCString(cbuf
, d
, &len
);
1664 char* js::Int32ToCString(Int32ToCStringBuf
* cbuf
, int32_t value
,
1667 char* s
= ::Int32ToCString(cbuf
, value
, &len
);
1675 char* js::Uint32ToCString(Int32ToCStringBuf
* cbuf
, uint32_t value
,
1678 char* s
= ::Int32ToCString(cbuf
, value
, &len
);
1686 char* js::Uint32ToHexCString(Int32ToCStringBuf
* cbuf
, uint32_t value
,
1689 char* s
= ::Int32ToCString
<uint32_t, 16>(cbuf
, value
, &len
);
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();
1704 if (NumberEqualsInt32(d
, &i
)) {
1705 bool isBase10Int
= (base
== 10);
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
)) {
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);
1726 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(base
, d
)) {
1730 // Plus three to include the largest number, the sign, and the terminating
1732 constexpr size_t MaximumLength
= std::numeric_limits
<int32_t>::digits
+ 3;
1734 char buf
[MaximumLength
] = {};
1736 char* numStr
= Int32ToCStringWithBase(buf
, i
, &numStrLen
, base
);
1737 MOZ_ASSERT(numStrLen
== strlen(numStr
));
1739 JSLinearString
* s
= NewStringCopyN
<allowGC
>(cx
, numStr
, numStrLen
);
1744 if (isBase10Int
&& i
>= 0) {
1745 s
->maybeInitializeIndexValue(i
);
1748 realm
->dtoaCache
.cache(base
, d
, s
);
1752 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(base
, d
)) {
1758 // We use a faster algorithm for base 10.
1761 char* numStr
= FracNumberToCString(&cbuf
, d
, &numStrLen
);
1763 MOZ_ASSERT(numStrLen
== strlen(numStr
));
1765 s
= NewStringCopyN
<allowGC
>(cx
, numStr
, numStrLen
);
1770 if (!EnsureDtoaState(cx
)) {
1771 if constexpr (allowGC
) {
1772 ReportOutOfMemory(cx
);
1777 UniqueChars
numStr(js_dtobasestr(cx
->dtoaState
, base
, d
));
1779 if constexpr (allowGC
) {
1780 ReportOutOfMemory(cx
);
1785 s
= NewStringCopyZ
<allowGC
>(cx
, numStr
.get());
1791 realm
->dtoaCache
.cache(base
, d
, 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
) {
1811 if (NumberEqualsInt32(d
, &si
)) {
1812 return Int32ToAtom(cx
, si
);
1815 if (JSLinearString
* str
= LookupDtoaCache(cx
, d
)) {
1816 return AtomizeString(cx
, str
);
1821 char* numStr
= FracNumberToCString(&cbuf
, d
, &length
);
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
);
1831 CacheNumber(cx
, d
, atom
);
1836 frontend::TaggedParserAtomIndex
js::NumberToParserAtom(
1837 FrontendContext
* fc
, frontend::ParserAtomsTable
& parserAtoms
, double d
) {
1839 if (NumberEqualsInt32(d
, &si
)) {
1840 return Int32ToParserAtom(fc
, parserAtoms
, si
);
1845 char* numStr
= FracNumberToCString(&cbuf
, d
, &length
);
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
)) {
1863 Latin1Char buffer
[JSFatInlineString::MAX_LENGTH_LATIN1
+ 1];
1864 RangedPtr
<Latin1Char
> end(buffer
+ JSFatInlineString::MAX_LENGTH_LATIN1
,
1865 buffer
, JSFatInlineString::MAX_LENGTH_LATIN1
+ 1);
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
);
1876 realm
->dtoaCache
.cache(10, index
, str
);
1880 JSString
* js::Int32ToStringWithBase(JSContext
* cx
, int32_t i
, int32_t base
,
1882 Rooted
<JSString
*> str(cx
, NumberToStringWithBase
<CanGC
>(cx
, double(i
), base
));
1889 return StringToUpperCase(cx
, str
);
1892 bool js::NumberValueToStringBuffer(const Value
& v
, StringBuffer
& sb
) {
1893 /* Convert to C-string. */
1898 cstr
= ::Int32ToCString(&cbuf
, v
.toInt32(), &cstrlen
);
1900 cstr
= NumberToCString(&cbuf
, v
.toDouble(), &cstrlen
);
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') {
1914 if (unicode::IsSpace(c
)) {
1917 return GenericNaN();
1920 template <typename CharT
>
1921 inline bool CharsToNonDecimalNumber(const CharT
* start
, const CharT
* end
,
1923 MOZ_ASSERT(end
- start
>= 2);
1924 MOZ_ASSERT(start
[0] == '0');
1927 if (start
[1] == 'b' || start
[1] == 'B') {
1929 } else if (start
[1] == 'o' || start
[1] == 'O') {
1931 } else if (start
[1] == 'x' || start
[1] == 'X') {
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
1940 const CharT
* endptr
;
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();
1952 template <typename CharT
>
1953 double js::CharsToNumber(const CharT
* chars
, size_t length
) {
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') {
1964 if (CharsToNonDecimalNumber(start
, end
, &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.
1977 double d
= js_strtod(start
, end
, &ep
);
1978 if (SkipSpace(ep
, end
) != end
) {
1979 return GenericNaN();
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
);
2005 *result
= LinearStringToNumber(linearStr
);
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();
2020 JS_PUBLIC_API
bool js::ToNumberSlow(JSContext
* cx
, HandleValue v_
,
2022 RootedValue
v(cx
, v_
);
2023 MOZ_ASSERT(!v
.isNumber());
2025 if (!v
.isPrimitive()) {
2026 if (!ToPrimitive(cx
, JSTYPE_NUMBER
, &v
)) {
2031 *out
= v
.toNumber();
2036 return StringToNumber(cx
, v
.toString(), out
);
2038 if (v
.isBoolean()) {
2039 *out
= v
.toBoolean() ? 1.0 : 0.0;
2046 if (v
.isUndefined()) {
2047 *out
= GenericNaN();
2050 #ifdef ENABLE_RECORD_TUPLE
2051 if (v
.isExtendedPrimitive()) {
2052 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2053 JSMSG_RECORD_TUPLE_TO_NUMBER
);
2058 MOZ_ASSERT(v
.isSymbol() || v
.isBigInt());
2059 unsigned errnum
= JSMSG_SYMBOL_TO_NUMBER
;
2061 errnum
= JSMSG_BIGINT_TO_NUMBER
;
2063 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, errnum
);
2067 // BigInt proposal section 3.1.6
2068 bool js::ToNumericSlow(JSContext
* cx
, MutableHandleValue vp
) {
2069 MOZ_ASSERT(!vp
.isNumeric());
2072 if (!vp
.isPrimitive()) {
2073 if (!ToPrimitive(cx
, JSTYPE_NUMBER
, vp
)) {
2079 if (vp
.isBigInt()) {
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
,
2093 MOZ_ASSERT(!v
.isInt32());
2098 if (!ToNumberSlow(cx
, v
, &d
)) {
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
2111 JS_PUBLIC_API
bool js::ToUint8Slow(JSContext
* cx
, const HandleValue v
,
2113 MOZ_ASSERT(!v
.isInt32());
2118 if (!ToNumberSlow(cx
, v
, &d
)) {
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
,
2132 MOZ_ASSERT(!v
.isInt32());
2137 if (!ToNumberSlow(cx
, v
, &d
)) {
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
,
2151 MOZ_ASSERT(!v
.isInt32());
2156 if (!ToNumberSlow(cx
, v
, &d
)) {
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
2169 JS_PUBLIC_API
bool js::ToUint64Slow(JSContext
* cx
, const HandleValue v
,
2171 MOZ_ASSERT(!v
.isInt32());
2176 if (!ToNumberSlow(cx
, v
, &d
)) {
2184 JS_PUBLIC_API
bool js::ToInt32Slow(JSContext
* cx
, const HandleValue v
,
2186 MOZ_ASSERT(!v
.isInt32());
2191 if (!ToNumberSlow(cx
, v
, &d
)) {
2199 bool js::ToInt32OrBigIntSlow(JSContext
* cx
, MutableHandleValue vp
) {
2200 MOZ_ASSERT(!vp
.isInt32());
2201 if (vp
.isDouble()) {
2202 vp
.setInt32(ToInt32(vp
.toDouble()));
2206 if (!ToNumeric(cx
, vp
)) {
2210 if (vp
.isBigInt()) {
2214 vp
.setInt32(ToInt32(vp
.toNumber()));
2218 JS_PUBLIC_API
bool js::ToUint32Slow(JSContext
* cx
, const HandleValue v
,
2220 MOZ_ASSERT(!v
.isInt32());
2225 if (!ToNumberSlow(cx
, v
, &d
)) {
2233 JS_PUBLIC_API
bool js::ToUint16Slow(JSContext
* cx
, const HandleValue v
,
2235 MOZ_ASSERT(!v
.isInt32());
2239 } else if (!ToNumberSlow(cx
, v
, &d
)) {
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);
2252 if (v
.isUndefined()) {
2258 double integerIndex
;
2259 if (!ToInteger(cx
, v
, &integerIndex
)) {
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
);
2273 *index
= uint64_t(integerIndex
);
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
);
2295 if constexpr (std::is_same_v
<CharT
, char16_t
>) {
2296 d
= converter
.StringToDouble(reinterpret_cast<const uc16
*>(s
), lengthInt
,
2299 static_assert(std::is_same_v
<CharT
, Latin1Char
>);
2300 d
= converter
.StringToDouble(reinterpret_cast<const char*>(s
), lengthInt
,
2303 MOZ_ASSERT(processed
>= 0);
2304 MOZ_ASSERT(processed
<= lengthInt
);
2306 if (processed
> 0) {
2307 *dEnd
= s
+ processed
;
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
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
== '+') {
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>();
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
);