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/FloatingPoint.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/RangedPtr.h"
16 #include "mozilla/TextUtils.h"
17 #include "mozilla/Utf8.h"
20 #ifdef HAVE_LOCALECONV
24 #include <string.h> // memmove
28 #include "double-conversion/double-conversion.h"
29 #include "frontend/ParserAtom.h" // frontend::{ParserAtomsTable, TaggedParserAtomIndex}
30 #include "jit/InlinableNatives.h"
31 #include "js/CharacterEncoding.h"
32 #include "js/Conversions.h"
33 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
35 # include "js/LocaleSensitive.h"
37 #include "js/PropertyAndElement.h" // JS_DefineFunctions
38 #include "js/PropertySpec.h"
39 #include "util/DoubleToString.h"
40 #include "util/Memory.h"
41 #include "util/StringBuffer.h"
42 #include "vm/BigIntType.h"
43 #include "vm/GlobalObject.h"
44 #include "vm/JSAtom.h"
45 #include "vm/JSContext.h"
46 #include "vm/JSObject.h"
47 #include "vm/StaticStrings.h"
48 #include "vm/WellKnownAtom.h" // js_*_str
50 #include "vm/Compartment-inl.h" // For js::UnwrapAndTypeCheckThis
51 #include "vm/NativeObject-inl.h"
52 #include "vm/NumberObject-inl.h"
53 #include "vm/StringType-inl.h"
58 using mozilla::AsciiAlphanumericToNumber
;
59 using mozilla::IsAsciiAlphanumeric
;
60 using mozilla::IsAsciiDigit
;
62 using mozilla::MinNumberValue
;
63 using mozilla::NegativeInfinity
;
64 using mozilla::NumberEqualsInt32
;
65 using mozilla::PositiveInfinity
;
66 using mozilla::RangedPtr
;
67 using mozilla::Utf8AsUnsignedChars
;
68 using mozilla::Utf8Unit
;
70 using JS::AutoCheckCannotGC
;
81 static bool EnsureDtoaState(JSContext
* cx
) {
83 cx
->dtoaState
= NewDtoaState();
91 template <typename CharT
>
92 static inline void AssertWellPlacedNumericSeparator(const CharT
* s
,
95 MOZ_ASSERT(start
< end
, "string is non-empty");
96 MOZ_ASSERT(s
> start
, "number can't start with a separator");
97 MOZ_ASSERT(s
+ 1 < end
,
98 "final character in a numeric literal can't be a separator");
99 MOZ_ASSERT(*(s
+ 1) != '_',
100 "separator can't be followed by another separator");
101 MOZ_ASSERT(*(s
- 1) != '_',
102 "separator can't be preceded by another separator");
106 * If we're accumulating a decimal number and the number is >= 2^53, then the
107 * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
108 * Call js_strtod_harder to get the correct answer.
110 template <typename CharT
>
111 static bool ComputeAccurateDecimalInteger(JSContext
* cx
, const CharT
* start
,
112 const CharT
* end
, double* dp
) {
113 size_t length
= end
- start
;
114 auto cstr
= cx
->make_pod_array
<char>(length
+ 1);
120 for (size_t i
= 0; i
< length
; i
++) {
121 char c
= char(start
[i
]);
123 AssertWellPlacedNumericSeparator(start
+ i
, start
, end
);
126 MOZ_ASSERT(IsAsciiAlphanumeric(c
));
131 if (!EnsureDtoaState(cx
)) {
136 *dp
= js_strtod_harder(cx
->dtoaState
, cstr
.get(), &estr
);
143 template <typename CharT
>
144 class BinaryDigitReader
{
145 const int base
; /* Base of number; must be a power of 2 */
146 int digit
; /* Current digit value in radix given by base */
147 int digitMask
; /* Mask to extract the next bit from digit */
148 const CharT
* cur
; /* Pointer to the remaining digits */
149 const CharT
* start
; /* Pointer to the start of the string */
150 const CharT
* end
; /* Pointer to first non-digit */
153 BinaryDigitReader(int base
, const CharT
* start
, const CharT
* end
)
161 /* Return the next binary digit from the number, or -1 if done. */
163 if (digitMask
== 0) {
170 AssertWellPlacedNumericSeparator(cur
- 1, start
, end
);
174 MOZ_ASSERT(IsAsciiAlphanumeric(c
));
175 digit
= AsciiAlphanumericToNumber(c
);
176 digitMask
= base
>> 1;
179 int bit
= (digit
& digitMask
) != 0;
185 } /* anonymous namespace */
188 * The fast result might also have been inaccurate for power-of-two bases. This
189 * happens if the addition in value * 2 + digit causes a round-down to an even
190 * least significant mantissa bit when the first dropped bit is a one. If any
191 * of the following digits in the number (which haven't been added in yet) are
192 * nonzero, then the correct action would have been to round up instead of
193 * down. An example occurs when reading the number 0x1000000000000081, which
194 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
196 template <typename CharT
>
197 static double ComputeAccurateBinaryBaseInteger(const CharT
* start
,
198 const CharT
* end
, int base
) {
199 BinaryDigitReader
<CharT
> bdr(base
, start
, end
);
201 /* Skip leading zeroes. */
204 bit
= bdr
.nextDigit();
207 MOZ_ASSERT(bit
== 1); // guaranteed by Get{Prefix,Decimal}Integer
209 /* Gather the 53 significant bits (including the leading 1). */
211 for (int j
= 52; j
> 0; j
--) {
212 bit
= bdr
.nextDigit();
216 value
= value
* 2 + bit
;
219 /* bit2 is the 54th bit (the first dropped from the mantissa). */
220 int bit2
= bdr
.nextDigit();
223 int sticky
= 0; /* sticky is 1 if any bit beyond the 54th is 1 */
226 while ((bit3
= bdr
.nextDigit()) >= 0) {
230 value
+= bit2
& (bit
| sticky
);
237 template <typename CharT
>
238 double js::ParseDecimalNumber(const mozilla::Range
<const CharT
> chars
) {
239 MOZ_ASSERT(chars
.length() > 0);
241 RangedPtr
<const CharT
> s
= chars
.begin(), end
= chars
.end();
244 MOZ_ASSERT('0' <= c
&& c
<= '9');
245 uint8_t digit
= c
- '0';
246 uint64_t next
= dec
* 10 + digit
;
247 MOZ_ASSERT(next
< DOUBLE_INTEGRAL_PRECISION_LIMIT
,
248 "next value won't be an integrally-precise double");
251 return static_cast<double>(dec
);
254 template double js::ParseDecimalNumber(
255 const mozilla::Range
<const Latin1Char
> chars
);
257 template double js::ParseDecimalNumber(
258 const mozilla::Range
<const char16_t
> chars
);
260 template <typename CharT
>
261 static bool GetPrefixInteger(const CharT
* start
, const CharT
* end
, int base
,
262 IntegerSeparatorHandling separatorHandling
,
263 const CharT
** endp
, double* dp
) {
264 MOZ_ASSERT(start
<= end
);
265 MOZ_ASSERT(2 <= base
&& base
<= 36);
267 const CharT
* s
= start
;
269 for (; s
< end
; s
++) {
271 if (!IsAsciiAlphanumeric(c
)) {
273 separatorHandling
== IntegerSeparatorHandling::SkipUnderscore
) {
274 AssertWellPlacedNumericSeparator(s
, start
, end
);
280 uint8_t digit
= AsciiAlphanumericToNumber(c
);
285 d
= d
* base
+ digit
;
291 /* If we haven't reached the limit of integer precision, we're done. */
292 if (d
< DOUBLE_INTEGRAL_PRECISION_LIMIT
) {
297 * Otherwise compute the correct integer from the prefix of valid digits
298 * if we're computing for base ten or a power of two. Don't worry about
299 * other bases; see ES2018, 18.2.5 `parseInt(string, radix)`, step 13.
305 if ((base
& (base
- 1)) == 0) {
306 *dp
= ComputeAccurateBinaryBaseInteger(start
, s
, base
);
312 template <typename CharT
>
313 bool js::GetPrefixInteger(JSContext
* cx
, const CharT
* start
, const CharT
* end
,
314 int base
, IntegerSeparatorHandling separatorHandling
,
315 const CharT
** endp
, double* dp
) {
316 if (::GetPrefixInteger(start
, end
, base
, separatorHandling
, endp
, dp
)) {
320 // Can only fail for base 10.
321 MOZ_ASSERT(base
== 10);
323 return ComputeAccurateDecimalInteger(cx
, start
, *endp
, dp
);
328 template bool GetPrefixInteger(JSContext
* cx
, const char16_t
* start
,
329 const char16_t
* end
, int base
,
330 IntegerSeparatorHandling separatorHandling
,
331 const char16_t
** endp
, double* dp
);
333 template bool GetPrefixInteger(JSContext
* cx
, const Latin1Char
* start
,
334 const Latin1Char
* end
, int base
,
335 IntegerSeparatorHandling separatorHandling
,
336 const Latin1Char
** endp
, double* dp
);
340 template <typename CharT
>
341 bool js::GetDecimalInteger(JSContext
* cx
, const CharT
* start
, const CharT
* end
,
343 MOZ_ASSERT(start
<= end
);
345 const CharT
* s
= start
;
347 for (; s
< end
; s
++) {
350 AssertWellPlacedNumericSeparator(s
, start
, end
);
353 MOZ_ASSERT(IsAsciiDigit(c
));
360 // If we haven't reached the limit of integer precision, we're done.
361 if (d
< DOUBLE_INTEGRAL_PRECISION_LIMIT
) {
365 // Otherwise compute the correct integer from the prefix of valid digits.
366 return ComputeAccurateDecimalInteger(cx
, start
, s
, dp
);
371 template bool GetDecimalInteger(JSContext
* cx
, const char16_t
* start
,
372 const char16_t
* end
, double* dp
);
374 template bool GetDecimalInteger(JSContext
* cx
, const Latin1Char
* start
,
375 const Latin1Char
* end
, double* dp
);
378 bool GetDecimalInteger
<Utf8Unit
>(JSContext
* cx
, const Utf8Unit
* start
,
379 const Utf8Unit
* end
, double* dp
) {
380 return GetDecimalInteger(cx
, Utf8AsUnsignedChars(start
),
381 Utf8AsUnsignedChars(end
), dp
);
386 template <typename CharT
>
387 bool js::GetDecimalNonInteger(JSContext
* cx
, const CharT
* start
,
388 const CharT
* end
, double* dp
) {
389 MOZ_ASSERT(start
<= end
);
391 size_t length
= end
- start
;
392 Vector
<char, 32> chars(cx
);
393 if (!chars
.growByUninitialized(length
+ 1)) {
397 const CharT
* s
= start
;
399 for (; s
< end
; s
++) {
402 AssertWellPlacedNumericSeparator(s
, start
, end
);
405 MOZ_ASSERT(IsAsciiDigit(c
) || c
== '.' || c
== 'e' || c
== 'E' ||
406 c
== '+' || c
== '-');
407 chars
[i
++] = char(c
);
411 if (!EnsureDtoaState(cx
)) {
416 *dp
= js_strtod_harder(cx
->dtoaState
, chars
.begin(), &ep
);
417 MOZ_ASSERT(ep
>= chars
.begin());
424 template bool GetDecimalNonInteger(JSContext
* cx
, const char16_t
* start
,
425 const char16_t
* end
, double* dp
);
427 template bool GetDecimalNonInteger(JSContext
* cx
, const Latin1Char
* start
,
428 const Latin1Char
* end
, double* dp
);
431 bool GetDecimalNonInteger
<Utf8Unit
>(JSContext
* cx
, const Utf8Unit
* start
,
432 const Utf8Unit
* end
, double* dp
) {
433 return GetDecimalNonInteger(cx
, Utf8AsUnsignedChars(start
),
434 Utf8AsUnsignedChars(end
), dp
);
439 static bool num_parseFloat(JSContext
* cx
, unsigned argc
, Value
* vp
) {
440 CallArgs args
= CallArgsFromVp(argc
, vp
);
442 if (args
.length() == 0) {
443 args
.rval().setNaN();
447 if (args
[0].isNumber()) {
448 // ToString(-0) is "0", handle it accordingly.
449 if (args
[0].isDouble() && args
[0].toDouble() == 0.0) {
450 args
.rval().setInt32(0);
452 args
.rval().set(args
[0]);
457 JSString
* str
= ToString
<CanGC
>(cx
, args
[0]);
462 if (str
->hasIndexValue()) {
463 args
.rval().setNumber(str
->getIndexValue());
467 JSLinearString
* linear
= str
->ensureLinear(cx
);
473 AutoCheckCannotGC nogc
;
474 if (linear
->hasLatin1Chars()) {
475 const Latin1Char
* begin
= linear
->latin1Chars(nogc
);
476 const Latin1Char
* end
;
477 if (!js_strtod(cx
, begin
, begin
+ linear
->length(), &end
, &d
)) {
484 const char16_t
* begin
= linear
->twoByteChars(nogc
);
486 if (!js_strtod(cx
, begin
, begin
+ linear
->length(), &end
, &d
)) {
494 args
.rval().setDouble(d
);
498 template <typename CharT
>
499 static bool ParseIntImpl(JSContext
* cx
, const CharT
* chars
, size_t length
,
500 bool stripPrefix
, int32_t radix
, double* res
) {
502 const CharT
* end
= chars
+ length
;
503 const CharT
* s
= SkipSpace(chars
, end
);
505 MOZ_ASSERT(chars
<= s
);
506 MOZ_ASSERT(s
<= end
);
509 bool negative
= (s
!= end
&& s
[0] == '-');
512 if (s
!= end
&& (s
[0] == '-' || s
[0] == '+')) {
518 if (end
- s
>= 2 && s
[0] == '0' && (s
[1] == 'x' || s
[1] == 'X')) {
525 const CharT
* actualEnd
;
527 if (!GetPrefixInteger(cx
, s
, end
, radix
, IntegerSeparatorHandling::None
,
532 if (s
== actualEnd
) {
535 *res
= negative
? -d
: d
;
541 static bool num_parseInt(JSContext
* cx
, unsigned argc
, Value
* vp
) {
542 CallArgs args
= CallArgsFromVp(argc
, vp
);
544 /* Fast paths and exceptional cases. */
545 if (args
.length() == 0) {
546 args
.rval().setNaN();
550 if (args
.length() == 1 || (args
[1].isInt32() && (args
[1].toInt32() == 0 ||
551 args
[1].toInt32() == 10))) {
552 if (args
[0].isInt32()) {
553 args
.rval().set(args
[0]);
558 * Step 1 is |inputString = ToString(string)|. When string >=
559 * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
560 * the word, which would mean the result of parseInt(string) should be |N|.
562 * To preserve this behaviour, we can't use the fast-path when string >
563 * 1e21, or else the result would be |NeM|.
565 * The same goes for values smaller than 1.0e-6, because the string would be
566 * in the form of "Ne-M".
568 if (args
[0].isDouble()) {
569 double d
= args
[0].toDouble();
570 if (1.0e-6 < d
&& d
< 1.0e21
) {
571 args
.rval().setNumber(floor(d
));
574 if (-1.0e21
< d
&& d
< -1.0e-6) {
575 args
.rval().setNumber(-floor(-d
));
579 args
.rval().setInt32(0);
584 if (args
[0].isString()) {
585 JSString
* str
= args
[0].toString();
586 if (str
->hasIndexValue()) {
587 args
.rval().setNumber(str
->getIndexValue());
594 RootedString
inputString(cx
, ToString
<CanGC
>(cx
, args
[0]));
598 args
[0].setString(inputString
);
601 bool stripPrefix
= true;
603 if (!args
.hasDefined(1)) {
606 if (!ToInt32(cx
, args
[1], &radix
)) {
612 if (radix
< 2 || radix
> 36) {
613 args
.rval().setNaN();
622 JSLinearString
* linear
= inputString
->ensureLinear(cx
);
627 AutoCheckCannotGC nogc
;
628 size_t length
= inputString
->length();
630 if (linear
->hasLatin1Chars()) {
631 if (!ParseIntImpl(cx
, linear
->latin1Chars(nogc
), length
, stripPrefix
, radix
,
636 if (!ParseIntImpl(cx
, linear
->twoByteChars(nogc
), length
, stripPrefix
,
642 args
.rval().setNumber(number
);
646 static const JSFunctionSpec number_functions
[] = {
647 JS_SELF_HOSTED_FN(js_isNaN_str
, "Global_isNaN", 1, JSPROP_RESOLVING
),
648 JS_SELF_HOSTED_FN(js_isFinite_str
, "Global_isFinite", 1, JSPROP_RESOLVING
),
651 const JSClass
NumberObject::class_
= {
653 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number
),
654 JS_NULL_CLASS_OPS
, &NumberObject::classSpec_
};
656 static bool Number(JSContext
* cx
, unsigned argc
, Value
* vp
) {
657 CallArgs args
= CallArgsFromVp(argc
, vp
);
659 if (args
.length() > 0) {
660 // BigInt proposal section 6.2, steps 2a-c.
661 if (!ToNumeric(cx
, args
[0])) {
664 if (args
[0].isBigInt()) {
665 args
[0].setNumber(BigInt::numberValue(args
[0].toBigInt()));
667 MOZ_ASSERT(args
[0].isNumber());
670 if (!args
.isConstructing()) {
671 if (args
.length() > 0) {
672 args
.rval().set(args
[0]);
674 args
.rval().setInt32(0);
679 RootedObject
proto(cx
);
680 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_Number
, &proto
)) {
684 double d
= args
.length() > 0 ? args
[0].toNumber() : 0;
685 JSObject
* obj
= NumberObject::create(cx
, d
, proto
);
689 args
.rval().setObject(*obj
);
693 // ES2020 draft rev e08b018785606bc6465a0456a79604b149007932
694 // 20.1.3 Properties of the Number Prototype Object, thisNumberValue.
696 static bool ThisNumberValue(JSContext
* cx
, const CallArgs
& args
,
697 const char* methodName
, double* number
) {
698 HandleValue thisv
= args
.thisv();
701 if (thisv
.isNumber()) {
702 *number
= thisv
.toNumber();
707 auto* obj
= UnwrapAndTypeCheckThis
<NumberObject
>(cx
, args
, methodName
);
712 *number
= obj
->unbox();
716 // On-off helper function for the self-hosted Number_toLocaleString method.
717 // This only exists to produce an error message with the right method name.
718 bool js::ThisNumberValueForToLocaleString(JSContext
* cx
, unsigned argc
,
720 CallArgs args
= CallArgsFromVp(argc
, vp
);
723 if (!ThisNumberValue(cx
, args
, "toLocaleString", &d
)) {
727 args
.rval().setNumber(d
);
731 static bool num_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
732 CallArgs args
= CallArgsFromVp(argc
, vp
);
735 if (!ThisNumberValue(cx
, args
, "toSource", &d
)) {
739 JSStringBuilder
sb(cx
);
740 if (!sb
.append("(new Number(") ||
741 !NumberValueToStringBuffer(cx
, NumberValue(d
), sb
) || !sb
.append("))")) {
745 JSString
* str
= sb
.finishString();
749 args
.rval().setString(str
);
753 ToCStringBuf::ToCStringBuf() : dbuf(nullptr) {
754 static_assert(sbufSize
>= DTOSTR_STANDARD_BUFFER_SIZE
,
755 "builtin space must be large enough to store even the "
756 "longest string produced by a conversion");
759 ToCStringBuf::~ToCStringBuf() { js_free(dbuf
); }
762 static JSLinearString
* LookupDtoaCache(JSContext
* cx
, double d
) {
763 if (Realm
* realm
= cx
->realm()) {
764 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(10, d
)) {
773 static void CacheNumber(JSContext
* cx
, double d
, JSLinearString
* str
) {
774 if (Realm
* realm
= cx
->realm()) {
775 realm
->dtoaCache
.cache(10, d
, str
);
780 static JSLinearString
* LookupInt32ToString(JSContext
* cx
, int32_t si
) {
781 if (si
>= 0 && StaticStrings::hasInt(si
)) {
782 return cx
->staticStrings().getInt(si
);
785 return LookupDtoaCache(cx
, si
);
788 template <typename T
>
789 MOZ_ALWAYS_INLINE
static T
* BackfillInt32InBuffer(int32_t si
, T
* buffer
,
790 size_t size
, size_t* length
) {
791 uint32_t ui
= Abs(si
);
792 MOZ_ASSERT_IF(si
== INT32_MIN
, ui
== uint32_t(INT32_MAX
) + 1);
794 RangedPtr
<T
> end(buffer
+ size
- 1, buffer
, size
);
796 RangedPtr
<T
> start
= BackfillIndexInCharBuffer(ui
, end
);
801 *length
= end
- start
;
805 template <AllowGC allowGC
>
806 JSLinearString
* js::Int32ToString(JSContext
* cx
, int32_t si
) {
807 if (JSLinearString
* str
= LookupInt32ToString(cx
, si
)) {
811 Latin1Char buffer
[JSFatInlineString::MAX_LENGTH_LATIN1
+ 1];
814 BackfillInt32InBuffer(si
, buffer
, std::size(buffer
), &length
);
816 mozilla::Range
<const Latin1Char
> chars(start
, length
);
817 JSInlineString
* str
=
818 NewInlineString
<allowGC
>(cx
, chars
, js::gc::DefaultHeap
);
823 str
->maybeInitializeIndexValue(si
);
826 CacheNumber(cx
, si
, str
);
830 template JSLinearString
* js::Int32ToString
<CanGC
>(JSContext
* cx
, int32_t si
);
832 template JSLinearString
* js::Int32ToString
<NoGC
>(JSContext
* cx
, int32_t si
);
834 JSLinearString
* js::Int32ToStringPure(JSContext
* cx
, int32_t si
) {
835 AutoUnsafeCallWithABI unsafe
;
836 return Int32ToString
<NoGC
>(cx
, si
);
839 JSAtom
* js::Int32ToAtom(JSContext
* cx
, int32_t si
) {
840 if (JSLinearString
* str
= LookupInt32ToString(cx
, si
)) {
841 return js::AtomizeString(cx
, str
);
844 char buffer
[JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1];
846 char* start
= BackfillInt32InBuffer(
847 si
, buffer
, JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1, &length
);
849 Maybe
<uint32_t> indexValue
;
851 indexValue
.emplace(si
);
854 JSAtom
* atom
= Atomize(cx
, start
, length
, indexValue
);
859 CacheNumber(cx
, si
, atom
);
863 frontend::TaggedParserAtomIndex
js::Int32ToParserAtom(
864 JSContext
* cx
, frontend::ParserAtomsTable
& parserAtoms
, int32_t si
) {
865 char buffer
[JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1];
867 char* start
= BackfillInt32InBuffer(
868 si
, buffer
, JSFatInlineString::MAX_LENGTH_TWO_BYTE
+ 1, &length
);
870 Maybe
<uint32_t> indexValue
;
872 indexValue
.emplace(si
);
875 return parserAtoms
.internAscii(cx
, start
, length
);
878 /* Returns a non-nullptr pointer to inside cbuf. */
879 static char* Int32ToCString(ToCStringBuf
* cbuf
, int32_t i
, size_t* len
,
883 RangedPtr
<char> cp(cbuf
->sbuf
+ ToCStringBuf::sbufSize
- 1, cbuf
->sbuf
,
884 ToCStringBuf::sbufSize
);
885 char* end
= cp
.get();
888 /* Build the string from behind. */
891 cp
= BackfillIndexInCharBuffer(u
, cp
);
895 unsigned newu
= u
/ 16;
896 *--cp
= "0123456789abcdef"[u
- newu
* 16];
901 MOZ_ASSERT(base
>= 2 && base
<= 36);
903 unsigned newu
= u
/ base
;
904 *--cp
= "0123456789abcdefghijklmnopqrstuvwxyz"[u
- newu
* base
];
913 *len
= end
- cp
.get();
917 template <AllowGC allowGC
>
918 static JSString
* NumberToStringWithBase(JSContext
* cx
, double d
, int base
);
920 static bool num_toString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
921 CallArgs args
= CallArgsFromVp(argc
, vp
);
924 if (!ThisNumberValue(cx
, args
, "toString", &d
)) {
929 if (args
.hasDefined(0)) {
931 if (!ToInteger(cx
, args
[0], &d2
)) {
935 if (d2
< 2 || d2
> 36) {
936 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_RADIX
);
942 JSString
* str
= NumberToStringWithBase
<CanGC
>(cx
, d
, base
);
946 args
.rval().setString(str
);
951 static bool num_toLocaleString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
952 CallArgs args
= CallArgsFromVp(argc
, vp
);
955 if (!ThisNumberValue(cx
, args
, "toLocaleString", &d
)) {
959 RootedString
str(cx
, NumberToStringWithBase
<CanGC
>(cx
, d
, 10));
965 * Create the string, move back to bytes to make string twiddling
966 * a bit easier and so we can insert platform charset seperators.
968 UniqueChars numBytes
= EncodeAscii(cx
, str
);
972 const char* num
= numBytes
.get();
978 * Find the first non-integer value, whether it be a letter as in
979 * 'Infinity', a decimal point, or an 'e' from exponential notation.
981 const char* nint
= num
;
985 while (*nint
>= '0' && *nint
<= '9') {
988 int digits
= nint
- num
;
989 const char* end
= num
+ digits
;
991 args
.rval().setString(str
);
995 JSRuntime
* rt
= cx
->runtime();
996 size_t thousandsLength
= strlen(rt
->thousandsSeparator
);
997 size_t decimalLength
= strlen(rt
->decimalSeparator
);
999 /* Figure out how long resulting string will be. */
1000 int buflen
= strlen(num
);
1002 buflen
+= decimalLength
- 1; /* -1 to account for existing '.' */
1005 const char* numGrouping
;
1006 const char* tmpGroup
;
1007 numGrouping
= tmpGroup
= rt
->numGrouping
;
1008 int remainder
= digits
;
1013 while (*tmpGroup
!= CHAR_MAX
&& *tmpGroup
!= '\0') {
1014 if (*tmpGroup
>= remainder
) {
1017 buflen
+= thousandsLength
;
1018 remainder
-= *tmpGroup
;
1023 if (*tmpGroup
== '\0' && *numGrouping
!= '\0') {
1024 nrepeat
= (remainder
- 1) / tmpGroup
[-1];
1025 buflen
+= thousandsLength
* nrepeat
;
1026 remainder
-= nrepeat
* tmpGroup
[-1];
1032 char* buf
= cx
->pod_malloc
<char>(buflen
+ 1);
1037 char* tmpDest
= buf
;
1038 const char* tmpSrc
= num
;
1040 while (*tmpSrc
== '-' || remainder
--) {
1041 MOZ_ASSERT(tmpDest
- buf
< buflen
);
1042 *tmpDest
++ = *tmpSrc
++;
1044 while (tmpSrc
< end
) {
1045 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(thousandsLength
) <= buflen
);
1046 strcpy(tmpDest
, rt
->thousandsSeparator
);
1047 tmpDest
+= thousandsLength
;
1048 MOZ_ASSERT(tmpDest
- buf
+ *tmpGroup
<= buflen
);
1049 js_memcpy(tmpDest
, tmpSrc
, *tmpGroup
);
1050 tmpDest
+= *tmpGroup
;
1051 tmpSrc
+= *tmpGroup
;
1052 if (--nrepeat
< 0) {
1058 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(decimalLength
) <= buflen
);
1059 strcpy(tmpDest
, rt
->decimalSeparator
);
1060 tmpDest
+= decimalLength
;
1061 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(strlen(nint
+ 1)) <= buflen
);
1062 strcpy(tmpDest
, nint
+ 1);
1064 MOZ_ASSERT(tmpDest
- buf
+ ptrdiff_t(strlen(nint
)) <= buflen
);
1065 strcpy(tmpDest
, nint
);
1068 if (cx
->runtime()->localeCallbacks
&&
1069 cx
->runtime()->localeCallbacks
->localeToUnicode
) {
1070 Rooted
<Value
> v(cx
, StringValue(str
));
1071 bool ok
= !!cx
->runtime()->localeCallbacks
->localeToUnicode(cx
, buf
, &v
);
1079 str
= NewStringCopyN
<CanGC
>(cx
, buf
, buflen
);
1085 args
.rval().setString(str
);
1088 #endif /* !JS_HAS_INTL_API */
1090 bool js::num_valueOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1091 CallArgs args
= CallArgsFromVp(argc
, vp
);
1094 if (!ThisNumberValue(cx
, args
, "valueOf", &d
)) {
1098 args
.rval().setNumber(d
);
1102 static const unsigned MAX_PRECISION
= 100;
1104 static bool ComputePrecisionInRange(JSContext
* cx
, int minPrecision
,
1105 int maxPrecision
, double prec
,
1107 if (minPrecision
<= prec
&& prec
<= maxPrecision
) {
1108 *precision
= int(prec
);
1113 if (char* numStr
= NumberToCString(cx
, &cbuf
, prec
, 10)) {
1114 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1115 JSMSG_PRECISION_RANGE
, numStr
);
1120 static constexpr size_t DoubleToStrResultBufSize
= 128;
1122 template <typename Op
>
1123 [[nodiscard
]] static bool DoubleToStrResult(JSContext
* cx
, const CallArgs
& args
,
1125 char buf
[DoubleToStrResultBufSize
];
1127 const auto& converter
=
1128 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1129 double_conversion::StringBuilder
builder(buf
, sizeof(buf
));
1131 bool ok
= op(converter
, builder
);
1132 MOZ_RELEASE_ASSERT(ok
);
1134 const char* numStr
= builder
.Finalize();
1135 MOZ_ASSERT(numStr
== buf
);
1137 JSString
* str
= NewStringCopyZ
<CanGC
>(cx
, numStr
);
1142 args
.rval().setString(str
);
1146 // ES 2021 draft 21.1.3.3.
1147 static bool num_toFixed(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1148 CallArgs args
= CallArgsFromVp(argc
, vp
);
1152 if (!ThisNumberValue(cx
, args
, "toFixed", &d
)) {
1158 if (args
.length() == 0) {
1162 if (!ToInteger(cx
, args
[0], &prec
)) {
1166 if (!ComputePrecisionInRange(cx
, 0, MAX_PRECISION
, prec
, &precision
)) {
1172 if (mozilla::IsNaN(d
)) {
1173 args
.rval().setString(cx
->names().NaN
);
1176 if (mozilla::IsInfinite(d
)) {
1178 args
.rval().setString(cx
->names().Infinity
);
1182 args
.rval().setString(cx
->names().NegativeInfinity
);
1186 // Steps 7-10 for very large numbers.
1187 if (d
<= -1e21
|| d
>= 1e+21) {
1188 JSString
* s
= NumberToString
<CanGC
>(cx
, d
);
1193 args
.rval().setString(s
);
1199 // DoubleToStringConverter::ToFixed is documented as requiring a buffer size
1202 // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint + 1
1203 // (one additional character for the sign, one for the decimal point,
1204 // and one for the null terminator)
1206 // We already ensured there are at most 21 digits before the point, and
1207 // MAX_PRECISION digits after the point.
1208 static_assert(1 + 21 + 1 + MAX_PRECISION
+ 1 <= DoubleToStrResultBufSize
);
1210 // The double-conversion library by default has a kMaxFixedDigitsAfterPoint of
1211 // 60. Assert our modified version supports at least MAX_PRECISION (100).
1212 using DToSConverter
= double_conversion::DoubleToStringConverter
;
1213 static_assert(DToSConverter::kMaxFixedDigitsAfterPoint
>= MAX_PRECISION
);
1215 return DoubleToStrResult(cx
, args
, [&](auto& converter
, auto& builder
) {
1216 return converter
.ToFixed(d
, precision
, &builder
);
1220 // ES 2021 draft 21.1.3.2.
1221 static bool num_toExponential(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1222 CallArgs args
= CallArgsFromVp(argc
, vp
);
1226 if (!ThisNumberValue(cx
, args
, "toExponential", &d
)) {
1232 if (args
.hasDefined(0)) {
1233 if (!ToInteger(cx
, args
[0], &prec
)) {
1239 MOZ_ASSERT_IF(!args
.hasDefined(0), prec
== 0);
1242 if (mozilla::IsNaN(d
)) {
1243 args
.rval().setString(cx
->names().NaN
);
1246 if (mozilla::IsInfinite(d
)) {
1248 args
.rval().setString(cx
->names().Infinity
);
1252 args
.rval().setString(cx
->names().NegativeInfinity
);
1258 if (!ComputePrecisionInRange(cx
, 0, MAX_PRECISION
, prec
, &precision
)) {
1264 // DoubleToStringConverter::ToExponential is documented as adding at most 8
1265 // characters on top of the requested digits: "the sign, the digit before the
1266 // decimal point, the decimal point, the exponent character, the exponent's
1267 // sign, and at most 3 exponent digits". In addition, the buffer must be able
1268 // to hold the trailing '\0' character.
1269 static_assert(MAX_PRECISION
+ 8 + 1 <= DoubleToStrResultBufSize
);
1271 return DoubleToStrResult(cx
, args
, [&](auto& converter
, auto& builder
) {
1272 int requestedDigits
= args
.hasDefined(0) ? precision
: -1;
1273 return converter
.ToExponential(d
, requestedDigits
, &builder
);
1277 // ES 2021 draft 21.1.3.5.
1278 static bool num_toPrecision(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1279 CallArgs args
= CallArgsFromVp(argc
, vp
);
1283 if (!ThisNumberValue(cx
, args
, "toPrecision", &d
)) {
1288 if (!args
.hasDefined(0)) {
1289 JSString
* str
= NumberToStringWithBase
<CanGC
>(cx
, d
, 10);
1293 args
.rval().setString(str
);
1299 if (!ToInteger(cx
, args
[0], &prec
)) {
1304 if (mozilla::IsNaN(d
)) {
1305 args
.rval().setString(cx
->names().NaN
);
1308 if (mozilla::IsInfinite(d
)) {
1310 args
.rval().setString(cx
->names().Infinity
);
1314 args
.rval().setString(cx
->names().NegativeInfinity
);
1320 if (!ComputePrecisionInRange(cx
, 1, MAX_PRECISION
, prec
, &precision
)) {
1326 // DoubleToStringConverter::ToPrecision is documented as adding at most 7
1327 // characters on top of the requested digits: "the sign, the decimal point,
1328 // the exponent character, the exponent's sign, and at most 3 exponent
1329 // digits". In addition, the buffer must be able to hold the trailing '\0'
1331 static_assert(MAX_PRECISION
+ 7 + 1 <= DoubleToStrResultBufSize
);
1333 return DoubleToStrResult(cx
, args
, [&](auto& converter
, auto& builder
) {
1334 return converter
.ToPrecision(d
, precision
, &builder
);
1338 static const JSFunctionSpec number_methods
[] = {
1339 JS_FN(js_toSource_str
, num_toSource
, 0, 0),
1340 JS_INLINABLE_FN(js_toString_str
, num_toString
, 1, 0, NumberToString
),
1342 JS_SELF_HOSTED_FN(js_toLocaleString_str
, "Number_toLocaleString", 0, 0),
1344 JS_FN(js_toLocaleString_str
, num_toLocaleString
, 0, 0),
1346 JS_FN(js_valueOf_str
, num_valueOf
, 0, 0),
1347 JS_FN("toFixed", num_toFixed
, 1, 0),
1348 JS_FN("toExponential", num_toExponential
, 1, 0),
1349 JS_FN("toPrecision", num_toPrecision
, 1, 0),
1352 bool js::IsInteger(const Value
& val
) {
1353 return val
.isInt32() || IsInteger(val
.toDouble());
1356 bool js::IsInteger(double d
) {
1357 return mozilla::IsFinite(d
) && JS::ToInteger(d
) == d
;
1360 static const JSFunctionSpec number_static_methods
[] = {
1361 JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1, 0),
1362 JS_SELF_HOSTED_FN("isInteger", "Number_isInteger", 1, 0),
1363 JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1, 0),
1364 JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1, 0),
1367 static const JSPropertySpec number_static_properties
[] = {
1368 JS_DOUBLE_PS("POSITIVE_INFINITY", mozilla::PositiveInfinity
<double>(),
1369 JSPROP_READONLY
| JSPROP_PERMANENT
),
1370 JS_DOUBLE_PS("NEGATIVE_INFINITY", mozilla::NegativeInfinity
<double>(),
1371 JSPROP_READONLY
| JSPROP_PERMANENT
),
1372 JS_DOUBLE_PS("MAX_VALUE", 1.7976931348623157E+308,
1373 JSPROP_READONLY
| JSPROP_PERMANENT
),
1374 JS_DOUBLE_PS("MIN_VALUE", MinNumberValue
<double>(),
1375 JSPROP_READONLY
| JSPROP_PERMANENT
),
1376 /* ES6 (April 2014 draft) 20.1.2.6 */
1377 JS_DOUBLE_PS("MAX_SAFE_INTEGER", 9007199254740991,
1378 JSPROP_READONLY
| JSPROP_PERMANENT
),
1379 /* ES6 (April 2014 draft) 20.1.2.10 */
1380 JS_DOUBLE_PS("MIN_SAFE_INTEGER", -9007199254740991,
1381 JSPROP_READONLY
| JSPROP_PERMANENT
),
1382 /* ES6 (May 2013 draft) 15.7.3.7 */
1383 JS_DOUBLE_PS("EPSILON", 2.2204460492503130808472633361816e-16,
1384 JSPROP_READONLY
| JSPROP_PERMANENT
),
1387 bool js::InitRuntimeNumberState(JSRuntime
* rt
) {
1388 // XXX If JS_HAS_INTL_API becomes true all the time at some point,
1389 // js::InitRuntimeNumberState is no longer fallible, and we should
1390 // change its return type.
1391 #if !JS_HAS_INTL_API
1392 /* Copy locale-specific separators into the runtime strings. */
1393 const char* thousandsSeparator
;
1394 const char* decimalPoint
;
1395 const char* grouping
;
1396 # ifdef HAVE_LOCALECONV
1397 struct lconv
* locale
= localeconv();
1398 thousandsSeparator
= locale
->thousands_sep
;
1399 decimalPoint
= locale
->decimal_point
;
1400 grouping
= locale
->grouping
;
1402 thousandsSeparator
= getenv("LOCALE_THOUSANDS_SEP");
1403 decimalPoint
= getenv("LOCALE_DECIMAL_POINT");
1404 grouping
= getenv("LOCALE_GROUPING");
1406 if (!thousandsSeparator
) {
1407 thousandsSeparator
= "'";
1409 if (!decimalPoint
) {
1417 * We use single malloc to get the memory for all separator and grouping
1420 size_t thousandsSeparatorSize
= strlen(thousandsSeparator
) + 1;
1421 size_t decimalPointSize
= strlen(decimalPoint
) + 1;
1422 size_t groupingSize
= strlen(grouping
) + 1;
1424 char* storage
= js_pod_malloc
<char>(thousandsSeparatorSize
+
1425 decimalPointSize
+ groupingSize
);
1430 js_memcpy(storage
, thousandsSeparator
, thousandsSeparatorSize
);
1431 rt
->thousandsSeparator
= storage
;
1432 storage
+= thousandsSeparatorSize
;
1434 js_memcpy(storage
, decimalPoint
, decimalPointSize
);
1435 rt
->decimalSeparator
= storage
;
1436 storage
+= decimalPointSize
;
1438 js_memcpy(storage
, grouping
, groupingSize
);
1439 rt
->numGrouping
= grouping
;
1440 #endif /* !JS_HAS_INTL_API */
1444 void js::FinishRuntimeNumberState(JSRuntime
* rt
) {
1445 #if !JS_HAS_INTL_API
1447 * The free also releases the memory for decimalSeparator and numGrouping
1450 char* storage
= const_cast<char*>(rt
->thousandsSeparator
.ref());
1452 #endif // !JS_HAS_INTL_API
1455 JSObject
* NumberObject::createPrototype(JSContext
* cx
, JSProtoKey key
) {
1456 NumberObject
* numberProto
=
1457 GlobalObject::createBlankPrototype
<NumberObject
>(cx
, cx
->global());
1461 numberProto
->setPrimitiveValue(0);
1465 static bool NumberClassFinish(JSContext
* cx
, HandleObject ctor
,
1466 HandleObject proto
) {
1467 Handle
<GlobalObject
*> global
= cx
->global();
1469 if (!JS_DefineFunctions(cx
, global
, number_functions
)) {
1473 // Number.parseInt should be the same function object as global parseInt.
1474 RootedId
parseIntId(cx
, NameToId(cx
->names().parseInt
));
1475 JSFunction
* parseInt
=
1476 DefineFunction(cx
, global
, parseIntId
, num_parseInt
, 2, JSPROP_RESOLVING
);
1480 RootedValue
parseIntValue(cx
, ObjectValue(*parseInt
));
1481 if (!DefineDataProperty(cx
, ctor
, parseIntId
, parseIntValue
, 0)) {
1485 // Number.parseFloat should be the same function object as global
1487 RootedId
parseFloatId(cx
, NameToId(cx
->names().parseFloat
));
1488 JSFunction
* parseFloat
= DefineFunction(cx
, global
, parseFloatId
,
1489 num_parseFloat
, 1, JSPROP_RESOLVING
);
1493 RootedValue
parseFloatValue(cx
, ObjectValue(*parseFloat
));
1494 if (!DefineDataProperty(cx
, ctor
, parseFloatId
, parseFloatValue
, 0)) {
1498 RootedValue
valueNaN(cx
, JS::NaNValue());
1499 RootedValue
valueInfinity(cx
, JS::InfinityValue());
1501 if (!DefineDataProperty(
1502 cx
, ctor
, cx
->names().NaN
, valueNaN
,
1503 JSPROP_PERMANENT
| JSPROP_READONLY
| JSPROP_RESOLVING
)) {
1507 // ES5 15.1.1.1, 15.1.1.2
1508 if (!NativeDefineDataProperty(
1509 cx
, global
, cx
->names().NaN
, valueNaN
,
1510 JSPROP_PERMANENT
| JSPROP_READONLY
| JSPROP_RESOLVING
) ||
1511 !NativeDefineDataProperty(
1512 cx
, global
, cx
->names().Infinity
, valueInfinity
,
1513 JSPROP_PERMANENT
| JSPROP_READONLY
| JSPROP_RESOLVING
)) {
1520 const ClassSpec
NumberObject::classSpec_
= {
1521 GenericCreateConstructor
<Number
, 1, gc::AllocKind::FUNCTION
>,
1522 NumberObject::createPrototype
,
1523 number_static_methods
,
1524 number_static_properties
,
1529 static char* FracNumberToCString(JSContext
* cx
, ToCStringBuf
* cbuf
, double d
,
1534 MOZ_ASSERT(!NumberEqualsInt32(d
, &_
));
1541 * This is V8's implementation of the algorithm described in the
1544 * Printing floating-point numbers quickly and accurately with integers.
1545 * Florian Loitsch, PLDI 2010.
1547 const double_conversion::DoubleToStringConverter
& converter
=
1548 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1549 double_conversion::StringBuilder
builder(cbuf
->sbuf
,
1550 js::ToCStringBuf::sbufSize
);
1551 converter
.ToShortest(d
, &builder
);
1552 numStr
= builder
.Finalize();
1554 if (!EnsureDtoaState(cx
)) {
1557 numStr
= cbuf
->dbuf
= js_dtobasestr(cx
->dtoaState
, base
, d
);
1562 void JS::NumberToString(double d
, char (&out
)[MaximumNumberToStringLength
]) {
1564 if (NumberEqualsInt32(d
, &i
)) {
1567 char* loc
= Int32ToCString(&cbuf
, i
, &len
, 10);
1568 memmove(out
, loc
, len
);
1571 const double_conversion::DoubleToStringConverter
& converter
=
1572 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1574 double_conversion::StringBuilder
builder(out
, sizeof(out
));
1575 converter
.ToShortest(d
, &builder
);
1581 MOZ_ASSERT(out
== result
);
1585 char* js::NumberToCString(JSContext
* cx
, ToCStringBuf
* cbuf
, double d
,
1586 int base
/* = 10*/) {
1589 return NumberEqualsInt32(d
, &i
) ? Int32ToCString(cbuf
, i
, &len
, base
)
1590 : FracNumberToCString(cx
, cbuf
, d
, base
);
1593 template <AllowGC allowGC
>
1594 static JSString
* NumberToStringWithBase(JSContext
* cx
, double d
, int base
) {
1595 MOZ_ASSERT(2 <= base
&& base
<= 36);
1601 Realm
* realm
= cx
->realm();
1604 bool isBase10Int
= false;
1605 if (NumberEqualsInt32(d
, &i
)) {
1606 isBase10Int
= (base
== 10);
1607 if (isBase10Int
&& StaticStrings::hasInt(i
)) {
1608 return cx
->staticStrings().getInt(i
);
1610 if (unsigned(i
) < unsigned(base
)) {
1612 return cx
->staticStrings().getInt(i
);
1614 char16_t c
= 'a' + i
- 10;
1615 MOZ_ASSERT(StaticStrings::hasUnit(c
));
1616 return cx
->staticStrings().getUnit(c
);
1618 if (unsigned(i
) < unsigned(base
* base
)) {
1619 static constexpr char digits
[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1620 char chars
[] = {digits
[i
/ base
], digits
[i
% base
]};
1621 JSString
* str
= cx
->staticStrings().lookup(chars
, 2);
1626 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(base
, d
)) {
1630 numStr
= Int32ToCString(&cbuf
, i
, &numStrLen
, base
);
1631 MOZ_ASSERT(!cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&&
1632 numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1633 MOZ_ASSERT(numStrLen
== strlen(numStr
));
1635 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(base
, d
)) {
1639 numStr
= FracNumberToCString(cx
, &cbuf
, d
, base
);
1641 if constexpr (allowGC
) {
1642 ReportOutOfMemory(cx
);
1646 MOZ_ASSERT_IF(base
== 10, !cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&&
1647 numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1648 MOZ_ASSERT_IF(base
!= 10, cbuf
.dbuf
&& cbuf
.dbuf
== numStr
);
1650 numStrLen
= strlen(numStr
);
1654 NewStringCopyN
<allowGC
>(cx
, numStr
, numStrLen
, js::gc::DefaultHeap
);
1659 if (isBase10Int
&& i
>= 0) {
1660 s
->maybeInitializeIndexValue(i
);
1663 realm
->dtoaCache
.cache(base
, d
, s
);
1667 template <AllowGC allowGC
>
1668 JSString
* js::NumberToString(JSContext
* cx
, double d
) {
1669 return NumberToStringWithBase
<allowGC
>(cx
, d
, 10);
1672 template JSString
* js::NumberToString
<CanGC
>(JSContext
* cx
, double d
);
1674 template JSString
* js::NumberToString
<NoGC
>(JSContext
* cx
, double d
);
1676 JSString
* js::NumberToStringPure(JSContext
* cx
, double d
) {
1677 AutoUnsafeCallWithABI unsafe
;
1678 return NumberToString
<NoGC
>(cx
, d
);
1681 JSAtom
* js::NumberToAtom(JSContext
* cx
, double d
) {
1683 if (NumberEqualsInt32(d
, &si
)) {
1684 return Int32ToAtom(cx
, si
);
1687 if (JSLinearString
* str
= LookupDtoaCache(cx
, d
)) {
1688 return AtomizeString(cx
, str
);
1692 char* numStr
= FracNumberToCString(cx
, &cbuf
, d
);
1694 ReportOutOfMemory(cx
);
1697 MOZ_ASSERT(!cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&&
1698 numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1700 size_t length
= strlen(numStr
);
1701 JSAtom
* atom
= Atomize(cx
, numStr
, length
);
1706 CacheNumber(cx
, d
, atom
);
1711 frontend::TaggedParserAtomIndex
js::NumberToParserAtom(
1712 JSContext
* cx
, frontend::ParserAtomsTable
& parserAtoms
, double d
) {
1714 if (NumberEqualsInt32(d
, &si
)) {
1715 return Int32ToParserAtom(cx
, parserAtoms
, si
);
1719 char* numStr
= FracNumberToCString(cx
, &cbuf
, d
);
1721 ReportOutOfMemory(cx
);
1722 return frontend::TaggedParserAtomIndex::null();
1724 MOZ_ASSERT(!cbuf
.dbuf
&& numStr
>= cbuf
.sbuf
&&
1725 numStr
< cbuf
.sbuf
+ cbuf
.sbufSize
);
1727 size_t length
= strlen(numStr
);
1728 return parserAtoms
.internAscii(cx
, numStr
, length
);
1731 JSLinearString
* js::IndexToString(JSContext
* cx
, uint32_t index
) {
1732 if (StaticStrings::hasUint(index
)) {
1733 return cx
->staticStrings().getUint(index
);
1736 Realm
* realm
= cx
->realm();
1737 if (JSLinearString
* str
= realm
->dtoaCache
.lookup(10, index
)) {
1741 Latin1Char buffer
[JSFatInlineString::MAX_LENGTH_LATIN1
+ 1];
1742 RangedPtr
<Latin1Char
> end(buffer
+ JSFatInlineString::MAX_LENGTH_LATIN1
,
1743 buffer
, JSFatInlineString::MAX_LENGTH_LATIN1
+ 1);
1745 RangedPtr
<Latin1Char
> start
= BackfillIndexInCharBuffer(index
, end
);
1747 mozilla::Range
<const Latin1Char
> chars(start
.get(), end
- start
);
1748 JSInlineString
* str
= NewInlineString
<CanGC
>(cx
, chars
, js::gc::DefaultHeap
);
1753 realm
->dtoaCache
.cache(10, index
, str
);
1757 bool js::NumberValueToStringBuffer(JSContext
* cx
, const Value
& v
,
1759 /* Convert to C-string. */
1764 cstr
= Int32ToCString(&cbuf
, v
.toInt32(), &cstrlen
);
1765 MOZ_ASSERT(cstrlen
== strlen(cstr
));
1767 cstr
= NumberToCString(cx
, &cbuf
, v
.toDouble());
1769 JS_ReportOutOfMemory(cx
);
1772 cstrlen
= strlen(cstr
);
1775 MOZ_ASSERT(!cbuf
.dbuf
&& cstrlen
< cbuf
.sbufSize
);
1776 return sb
.append(cstr
, cstrlen
);
1779 template <typename CharT
>
1780 inline void CharToNumber(CharT c
, double* result
) {
1781 if ('0' <= c
&& c
<= '9') {
1783 } else if (unicode::IsSpace(c
)) {
1786 *result
= GenericNaN();
1790 template <typename CharT
>
1791 inline bool CharsToNonDecimalNumber(const CharT
* start
, const CharT
* end
,
1793 MOZ_ASSERT(end
- start
>= 2);
1794 MOZ_ASSERT(start
[0] == '0');
1797 if (start
[1] == 'b' || start
[1] == 'B') {
1799 } else if (start
[1] == 'o' || start
[1] == 'O') {
1801 } else if (start
[1] == 'x' || start
[1] == 'X') {
1807 // It's probably a non-decimal number. Accept if there's at least one digit
1808 // after the 0b|0o|0x, and if no non-whitespace characters follow all the
1810 const CharT
* endptr
;
1812 MOZ_ALWAYS_TRUE(GetPrefixInteger(
1813 start
+ 2, end
, radix
, IntegerSeparatorHandling::None
, &endptr
, &d
));
1814 if (endptr
== start
+ 2 || SkipSpace(endptr
, end
) != end
) {
1815 *result
= GenericNaN();
1822 template <typename CharT
>
1823 bool js::CharsToNumber(JSContext
* cx
, const CharT
* chars
, size_t length
,
1826 CharToNumber(chars
[0], result
);
1830 const CharT
* end
= chars
+ length
;
1831 const CharT
* start
= SkipSpace(chars
, end
);
1833 // ECMA doesn't allow signed non-decimal numbers (bug 273467).
1834 if (end
- start
>= 2 && start
[0] == '0') {
1835 if (CharsToNonDecimalNumber(start
, end
, result
)) {
1841 * Note that ECMA doesn't treat a string beginning with a '0' as
1842 * an octal number here. This works because all such numbers will
1843 * be interpreted as decimal by js_strtod. Also, any hex numbers
1844 * that have made it here (which can only be negative ones) will
1845 * be treated as 0 without consuming the 'x' by js_strtod.
1849 if (!js_strtod(cx
, start
, end
, &ep
, &d
)) {
1850 *result
= GenericNaN();
1854 if (SkipSpace(ep
, end
) != end
) {
1855 *result
= GenericNaN();
1863 template bool js::CharsToNumber(JSContext
* cx
, const Latin1Char
* chars
,
1864 size_t length
, double* result
);
1866 template bool js::CharsToNumber(JSContext
* cx
, const char16_t
* chars
,
1867 size_t length
, double* result
);
1869 template <typename CharT
>
1870 static bool CharsToNumber(const CharT
* chars
, size_t length
, double* result
) {
1872 CharToNumber(chars
[0], result
);
1876 const CharT
* end
= chars
+ length
;
1877 const CharT
* start
= SkipSpace(chars
, end
);
1879 // ECMA doesn't allow signed non-decimal numbers (bug 273467).
1880 if (end
- start
>= 2 && start
[0] == '0') {
1881 if (CharsToNonDecimalNumber(start
, end
, result
)) {
1886 // It's probably a decimal number. Accept if no non-whitespace characters
1887 // follow all the digits.
1889 // NB: Fractional digits are not supported, because they require calling into
1890 // dtoa, which isn't possible without a JSContext.
1891 const CharT
* endptr
;
1893 if (!GetPrefixInteger(start
, end
, 10, IntegerSeparatorHandling::None
, &endptr
,
1895 SkipSpace(endptr
, end
) != end
) {
1903 bool js::StringToNumber(JSContext
* cx
, JSString
* str
, double* result
) {
1904 AutoCheckCannotGC nogc
;
1905 JSLinearString
* linearStr
= str
->ensureLinear(cx
);
1910 if (str
->hasIndexValue()) {
1911 *result
= str
->getIndexValue();
1915 return linearStr
->hasLatin1Chars()
1916 ? CharsToNumber(cx
, linearStr
->latin1Chars(nogc
), str
->length(),
1918 : CharsToNumber(cx
, linearStr
->twoByteChars(nogc
), str
->length(),
1922 bool js::StringToNumberPure(JSContext
* cx
, JSString
* str
, double* result
) {
1923 // IC Code calls this directly.
1924 AutoUnsafeCallWithABI unsafe
;
1926 if (!StringToNumber(cx
, str
, result
)) {
1927 cx
->recoverFromOutOfMemory();
1933 bool js::MaybeStringToNumber(JSLinearString
* str
, double* result
) {
1934 AutoCheckCannotGC nogc
;
1936 if (str
->hasIndexValue()) {
1937 *result
= str
->getIndexValue();
1941 return str
->hasLatin1Chars()
1942 ? ::CharsToNumber(str
->latin1Chars(nogc
), str
->length(), result
)
1943 : ::CharsToNumber(str
->twoByteChars(nogc
), str
->length(), result
);
1946 JS_PUBLIC_API
bool js::ToNumberSlow(JSContext
* cx
, HandleValue v_
,
1948 RootedValue
v(cx
, v_
);
1949 MOZ_ASSERT(!v
.isNumber());
1951 if (!v
.isPrimitive()) {
1952 if (cx
->isHelperThreadContext()) {
1956 if (!ToPrimitive(cx
, JSTYPE_NUMBER
, &v
)) {
1961 *out
= v
.toNumber();
1966 return StringToNumber(cx
, v
.toString(), out
);
1968 if (v
.isBoolean()) {
1969 *out
= v
.toBoolean() ? 1.0 : 0.0;
1976 if (v
.isUndefined()) {
1977 *out
= GenericNaN();
1980 #ifdef ENABLE_RECORD_TUPLE
1981 if (v
.isExtendedPrimitive()) {
1982 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1983 JSMSG_RECORD_TUPLE_TO_NUMBER
);
1988 MOZ_ASSERT(v
.isSymbol() || v
.isBigInt());
1989 if (!cx
->isHelperThreadContext()) {
1990 unsigned errnum
= JSMSG_SYMBOL_TO_NUMBER
;
1992 errnum
= JSMSG_BIGINT_TO_NUMBER
;
1994 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, errnum
);
1999 // BigInt proposal section 3.1.6
2000 bool js::ToNumericSlow(JSContext
* cx
, MutableHandleValue vp
) {
2001 MOZ_ASSERT(!vp
.isNumeric());
2004 if (!vp
.isPrimitive()) {
2005 if (cx
->isHelperThreadContext()) {
2008 if (!ToPrimitive(cx
, JSTYPE_NUMBER
, vp
)) {
2014 if (vp
.isBigInt()) {
2019 return ToNumber(cx
, vp
);
2023 * Convert a value to an int8_t, according to the WebIDL rules for byte
2024 * conversion. Return converted value in *out on success, false on failure.
2026 JS_PUBLIC_API
bool js::ToInt8Slow(JSContext
* cx
, const HandleValue v
,
2028 MOZ_ASSERT(!v
.isInt32());
2033 if (!ToNumberSlow(cx
, v
, &d
)) {
2042 * Convert a value to an uint8_t, according to the ToUInt8() function in ES6
2043 * ECMA-262, 7.1.10. Return converted value in *out on success, false on
2046 JS_PUBLIC_API
bool js::ToUint8Slow(JSContext
* cx
, const HandleValue v
,
2048 MOZ_ASSERT(!v
.isInt32());
2053 if (!ToNumberSlow(cx
, v
, &d
)) {
2062 * Convert a value to an int16_t, according to the WebIDL rules for short
2063 * conversion. Return converted value in *out on success, false on failure.
2065 JS_PUBLIC_API
bool js::ToInt16Slow(JSContext
* cx
, const HandleValue v
,
2067 MOZ_ASSERT(!v
.isInt32());
2072 if (!ToNumberSlow(cx
, v
, &d
)) {
2081 * Convert a value to an int64_t, according to the WebIDL rules for long long
2082 * conversion. Return converted value in *out on success, false on failure.
2084 JS_PUBLIC_API
bool js::ToInt64Slow(JSContext
* cx
, const HandleValue v
,
2086 MOZ_ASSERT(!v
.isInt32());
2091 if (!ToNumberSlow(cx
, v
, &d
)) {
2100 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned
2101 * long long conversion. Return converted value in *out on success, false on
2104 JS_PUBLIC_API
bool js::ToUint64Slow(JSContext
* cx
, const HandleValue v
,
2106 MOZ_ASSERT(!v
.isInt32());
2111 if (!ToNumberSlow(cx
, v
, &d
)) {
2119 JS_PUBLIC_API
bool js::ToInt32Slow(JSContext
* cx
, const HandleValue v
,
2121 MOZ_ASSERT(!v
.isInt32());
2126 if (!ToNumberSlow(cx
, v
, &d
)) {
2134 bool js::ToInt32OrBigIntSlow(JSContext
* cx
, MutableHandleValue vp
) {
2135 MOZ_ASSERT(!vp
.isInt32());
2136 if (vp
.isDouble()) {
2137 vp
.setInt32(ToInt32(vp
.toDouble()));
2141 if (!ToNumeric(cx
, vp
)) {
2145 if (vp
.isBigInt()) {
2149 vp
.setInt32(ToInt32(vp
.toNumber()));
2153 JS_PUBLIC_API
bool js::ToUint32Slow(JSContext
* cx
, const HandleValue v
,
2155 MOZ_ASSERT(!v
.isInt32());
2160 if (!ToNumberSlow(cx
, v
, &d
)) {
2168 JS_PUBLIC_API
bool js::ToUint16Slow(JSContext
* cx
, const HandleValue v
,
2170 MOZ_ASSERT(!v
.isInt32());
2174 } else if (!ToNumberSlow(cx
, v
, &d
)) {
2181 // ES2017 draft 7.1.17 ToIndex
2182 bool js::ToIndexSlow(JSContext
* cx
, JS::HandleValue v
,
2183 const unsigned errorNumber
, uint64_t* index
) {
2184 MOZ_ASSERT_IF(v
.isInt32(), v
.toInt32() < 0);
2187 if (v
.isUndefined()) {
2193 double integerIndex
;
2194 if (!ToInteger(cx
, v
, &integerIndex
)) {
2198 // Inlined version of ToLength.
2199 // 1. Already an integer.
2200 // 2. Step eliminates < 0, +0 == -0 with SameValueZero.
2201 // 3/4. Limit to <= 2^53-1, so everything above should fail.
2202 if (integerIndex
< 0 || integerIndex
>= DOUBLE_INTEGRAL_PRECISION_LIMIT
) {
2203 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, errorNumber
);
2208 *index
= uint64_t(integerIndex
);
2212 template <typename CharT
>
2213 bool js_strtod(JSContext
* cx
, const CharT
* begin
, const CharT
* end
,
2214 const CharT
** dEnd
, double* d
) {
2215 const CharT
* s
= SkipSpace(begin
, end
);
2216 size_t length
= end
- s
;
2218 Vector
<char, 32> chars(cx
);
2219 if (!chars
.growByUninitialized(length
+ 1)) {
2224 for (; i
< length
; i
++) {
2233 /* Try to parse +Infinity, -Infinity or Infinity. */
2235 char* afterSign
= chars
.begin();
2236 bool negative
= (*afterSign
== '-');
2237 if (negative
|| *afterSign
== '+') {
2241 if (*afterSign
== 'I' && !strncmp(afterSign
, "Infinity", 8)) {
2242 *d
= negative
? NegativeInfinity
<double>() : PositiveInfinity
<double>();
2243 *dEnd
= s
+ (afterSign
- chars
.begin()) + 8;
2248 if (!EnsureDtoaState(cx
)) {
2252 /* Everything else. */
2254 *d
= js_strtod_harder(cx
->dtoaState
, chars
.begin(), &ep
);
2256 MOZ_ASSERT(ep
>= chars
.begin());
2258 if (ep
== chars
.begin()) {
2261 *dEnd
= s
+ (ep
- chars
.begin());
2267 template bool js_strtod(JSContext
* cx
, const char16_t
* begin
,
2268 const char16_t
* end
, const char16_t
** dEnd
, double* d
);
2270 template bool js_strtod(JSContext
* cx
, const Latin1Char
* begin
,
2271 const Latin1Char
* end
, const Latin1Char
** dEnd
,