Bug 1852545 [wpt PR 41834] - [FedCM] Update wpt tests for AccountAutoSelectedFlag...
[gecko.git] / js / src / jsnum.cpp
blobb658f0a827b69364061e1c21449e2d9bbb6098a9
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS number type and wrapper class.
9 */
11 #include "jsnum.h"
13 #include "mozilla/Casting.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/Maybe.h"
16 #include "mozilla/RangedPtr.h"
17 #include "mozilla/TextUtils.h"
18 #include "mozilla/Utf8.h"
20 #include <algorithm>
21 #include <iterator>
22 #include <limits>
23 #ifdef HAVE_LOCALECONV
24 # include <locale.h>
25 #endif
26 #include <math.h>
27 #include <string.h> // memmove
28 #include <string_view>
30 #include "jstypes.h"
32 #include "double-conversion/double-conversion.h"
33 #include "frontend/ParserAtom.h" // frontend::{ParserAtomsTable, TaggedParserAtomIndex}
34 #include "jit/InlinableNatives.h"
35 #include "js/CharacterEncoding.h"
36 #include "js/Conversions.h"
37 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
38 #include "js/GCAPI.h"
39 #if !JS_HAS_INTL_API
40 # include "js/LocaleSensitive.h"
41 #endif
42 #include "js/PropertyAndElement.h" // JS_DefineFunctions
43 #include "js/PropertySpec.h"
44 #include "util/DoubleToString.h"
45 #include "util/Memory.h"
46 #include "util/StringBuffer.h"
47 #include "vm/BigIntType.h"
48 #include "vm/GlobalObject.h"
49 #include "vm/JSAtomUtils.h" // Atomize, AtomizeString
50 #include "vm/JSContext.h"
51 #include "vm/JSObject.h"
52 #include "vm/StaticStrings.h"
54 #include "vm/Compartment-inl.h" // For js::UnwrapAndTypeCheckThis
55 #include "vm/GeckoProfiler-inl.h"
56 #include "vm/JSAtomUtils-inl.h" // BackfillIndexInCharBuffer
57 #include "vm/NativeObject-inl.h"
58 #include "vm/NumberObject-inl.h"
59 #include "vm/StringType-inl.h"
61 using namespace js;
63 using mozilla::Abs;
64 using mozilla::AsciiAlphanumericToNumber;
65 using mozilla::IsAsciiAlphanumeric;
66 using mozilla::IsAsciiDigit;
67 using mozilla::Maybe;
68 using mozilla::MinNumberValue;
69 using mozilla::NegativeInfinity;
70 using mozilla::NumberEqualsInt32;
71 using mozilla::PositiveInfinity;
72 using mozilla::RangedPtr;
73 using mozilla::Utf8AsUnsignedChars;
74 using mozilla::Utf8Unit;
76 using JS::AutoCheckCannotGC;
77 using JS::GenericNaN;
78 using JS::ToInt16;
79 using JS::ToInt32;
80 using JS::ToInt64;
81 using JS::ToInt8;
82 using JS::ToUint16;
83 using JS::ToUint32;
84 using JS::ToUint64;
85 using JS::ToUint8;
87 static bool EnsureDtoaState(JSContext* cx) {
88 if (!cx->dtoaState) {
89 cx->dtoaState = NewDtoaState();
90 if (!cx->dtoaState) {
91 return false;
94 return true;
97 template <typename CharT>
98 static inline void AssertWellPlacedNumericSeparator(const CharT* s,
99 const CharT* start,
100 const CharT* end) {
101 MOZ_ASSERT(start < end, "string is non-empty");
102 MOZ_ASSERT(s > start, "number can't start with a separator");
103 MOZ_ASSERT(s + 1 < end,
104 "final character in a numeric literal can't be a separator");
105 MOZ_ASSERT(*(s + 1) != '_',
106 "separator can't be followed by another separator");
107 MOZ_ASSERT(*(s - 1) != '_',
108 "separator can't be preceded by another separator");
111 namespace {
113 template <typename CharT>
114 class BinaryDigitReader {
115 const int base; /* Base of number; must be a power of 2 */
116 int digit; /* Current digit value in radix given by base */
117 int digitMask; /* Mask to extract the next bit from digit */
118 const CharT* cur; /* Pointer to the remaining digits */
119 const CharT* start; /* Pointer to the start of the string */
120 const CharT* end; /* Pointer to first non-digit */
122 public:
123 BinaryDigitReader(int base, const CharT* start, const CharT* end)
124 : base(base),
125 digit(0),
126 digitMask(0),
127 cur(start),
128 start(start),
129 end(end) {}
131 /* Return the next binary digit from the number, or -1 if done. */
132 int nextDigit() {
133 if (digitMask == 0) {
134 if (cur == end) {
135 return -1;
138 int c = *cur++;
139 if (c == '_') {
140 AssertWellPlacedNumericSeparator(cur - 1, start, end);
141 c = *cur++;
144 MOZ_ASSERT(IsAsciiAlphanumeric(c));
145 digit = AsciiAlphanumericToNumber(c);
146 digitMask = base >> 1;
149 int bit = (digit & digitMask) != 0;
150 digitMask >>= 1;
151 return bit;
155 } /* anonymous namespace */
158 * The fast result might also have been inaccurate for power-of-two bases. This
159 * happens if the addition in value * 2 + digit causes a round-down to an even
160 * least significant mantissa bit when the first dropped bit is a one. If any
161 * of the following digits in the number (which haven't been added in yet) are
162 * nonzero, then the correct action would have been to round up instead of
163 * down. An example occurs when reading the number 0x1000000000000081, which
164 * rounds to 0x1000000000000000 instead of 0x1000000000000100.
166 template <typename CharT>
167 static double ComputeAccurateBinaryBaseInteger(const CharT* start,
168 const CharT* end, int base) {
169 BinaryDigitReader<CharT> bdr(base, start, end);
171 /* Skip leading zeroes. */
172 int bit;
173 do {
174 bit = bdr.nextDigit();
175 } while (bit == 0);
177 MOZ_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
179 /* Gather the 53 significant bits (including the leading 1). */
180 double value = 1.0;
181 for (int j = 52; j > 0; j--) {
182 bit = bdr.nextDigit();
183 if (bit < 0) {
184 return value;
186 value = value * 2 + bit;
189 /* bit2 is the 54th bit (the first dropped from the mantissa). */
190 int bit2 = bdr.nextDigit();
191 if (bit2 >= 0) {
192 double factor = 2.0;
193 int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
194 int bit3;
196 while ((bit3 = bdr.nextDigit()) >= 0) {
197 sticky |= bit3;
198 factor *= 2;
200 value += bit2 & (bit | sticky);
201 value *= factor;
204 return value;
207 template <typename CharT>
208 double js::ParseDecimalNumber(const mozilla::Range<const CharT> chars) {
209 MOZ_ASSERT(chars.length() > 0);
210 uint64_t dec = 0;
211 RangedPtr<const CharT> s = chars.begin(), end = chars.end();
212 do {
213 CharT c = *s;
214 MOZ_ASSERT('0' <= c && c <= '9');
215 uint8_t digit = c - '0';
216 uint64_t next = dec * 10 + digit;
217 MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
218 "next value won't be an integrally-precise double");
219 dec = next;
220 } while (++s < end);
221 return static_cast<double>(dec);
224 template double js::ParseDecimalNumber(
225 const mozilla::Range<const Latin1Char> chars);
227 template double js::ParseDecimalNumber(
228 const mozilla::Range<const char16_t> chars);
230 template <typename CharT>
231 static bool GetPrefixIntegerImpl(const CharT* start, const CharT* end, int base,
232 IntegerSeparatorHandling separatorHandling,
233 const CharT** endp, double* dp) {
234 MOZ_ASSERT(start <= end);
235 MOZ_ASSERT(2 <= base && base <= 36);
237 const CharT* s = start;
238 double d = 0.0;
239 for (; s < end; s++) {
240 CharT c = *s;
241 if (!IsAsciiAlphanumeric(c)) {
242 if (c == '_' &&
243 separatorHandling == IntegerSeparatorHandling::SkipUnderscore) {
244 AssertWellPlacedNumericSeparator(s, start, end);
245 continue;
247 break;
250 uint8_t digit = AsciiAlphanumericToNumber(c);
251 if (digit >= base) {
252 break;
255 d = d * base + digit;
258 *endp = s;
259 *dp = d;
261 /* If we haven't reached the limit of integer precision, we're done. */
262 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
263 return true;
267 * Otherwise compute the correct integer from the prefix of valid digits
268 * if we're computing for base ten or a power of two. Don't worry about
269 * other bases; see ES2018, 18.2.5 `parseInt(string, radix)`, step 13.
271 if (base == 10) {
272 return false;
275 if ((base & (base - 1)) == 0) {
276 *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
279 return true;
282 template <typename CharT>
283 bool js::GetPrefixInteger(const CharT* start, const CharT* end, int base,
284 IntegerSeparatorHandling separatorHandling,
285 const CharT** endp, double* dp) {
286 if (GetPrefixIntegerImpl(start, end, base, separatorHandling, endp, dp)) {
287 return true;
290 // Can only fail for base 10.
291 MOZ_ASSERT(base == 10);
293 // If we're accumulating a decimal number and the number is >= 2^53, then the
294 // fast result from the loop in GetPrefixIntegerImpl may be inaccurate. Call
295 // GetDecimal to get the correct answer.
296 return GetDecimal(start, *endp, dp);
299 namespace js {
301 template bool GetPrefixInteger(const char16_t* start, const char16_t* end,
302 int base,
303 IntegerSeparatorHandling separatorHandling,
304 const char16_t** endp, double* dp);
306 template bool GetPrefixInteger(const Latin1Char* start, const Latin1Char* end,
307 int base,
308 IntegerSeparatorHandling separatorHandling,
309 const Latin1Char** endp, double* dp);
311 } // namespace js
313 template <typename CharT>
314 bool js::GetDecimalInteger(const CharT* start, const CharT* end, double* dp) {
315 MOZ_ASSERT(start <= end);
317 double d = 0.0;
318 for (const CharT* s = start; s < end; s++) {
319 CharT c = *s;
320 if (c == '_') {
321 AssertWellPlacedNumericSeparator(s, start, end);
322 continue;
324 MOZ_ASSERT(IsAsciiDigit(c));
325 int digit = c - '0';
326 d = d * 10 + digit;
329 // If we haven't reached the limit of integer precision, we're done.
330 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
331 *dp = d;
332 return true;
335 // Otherwise compute the correct integer using GetDecimal.
336 return GetDecimal(start, end, dp);
339 namespace js {
341 template bool GetDecimalInteger(const char16_t* start, const char16_t* end,
342 double* dp);
344 template bool GetDecimalInteger(const Latin1Char* start, const Latin1Char* end,
345 double* dp);
347 template <>
348 bool GetDecimalInteger<Utf8Unit>(const Utf8Unit* start, const Utf8Unit* end,
349 double* dp) {
350 return GetDecimalInteger(Utf8AsUnsignedChars(start), Utf8AsUnsignedChars(end),
351 dp);
354 } // namespace js
356 template <typename CharT>
357 bool js::GetDecimal(const CharT* start, const CharT* end, double* dp) {
358 MOZ_ASSERT(start <= end);
360 size_t length = end - start;
362 auto convert = [](auto* chars, size_t length) -> double {
363 using SToDConverter = double_conversion::StringToDoubleConverter;
364 SToDConverter converter(/* flags = */ 0, /* empty_string_value = */ 0.0,
365 /* junk_string_value = */ 0.0,
366 /* infinity_symbol = */ nullptr,
367 /* nan_symbol = */ nullptr);
368 int lengthInt = mozilla::AssertedCast<int>(length);
369 int processed = 0;
370 double d = converter.StringToDouble(chars, lengthInt, &processed);
371 MOZ_ASSERT(processed >= 0);
372 MOZ_ASSERT(size_t(processed) == length);
373 return d;
376 // If there are no underscores, we don't need to copy the chars.
377 bool hasUnderscore = std::any_of(start, end, [](auto c) { return c == '_'; });
378 if (!hasUnderscore) {
379 if constexpr (std::is_same_v<CharT, char16_t>) {
380 *dp = convert(reinterpret_cast<const uc16*>(start), length);
381 } else {
382 static_assert(std::is_same_v<CharT, Latin1Char>);
383 *dp = convert(reinterpret_cast<const char*>(start), length);
385 return true;
388 Vector<char, 32, SystemAllocPolicy> chars;
389 if (!chars.growByUninitialized(length)) {
390 return false;
393 const CharT* s = start;
394 size_t i = 0;
395 for (; s < end; s++) {
396 CharT c = *s;
397 if (c == '_') {
398 AssertWellPlacedNumericSeparator(s, start, end);
399 continue;
401 MOZ_ASSERT(IsAsciiDigit(c) || c == '.' || c == 'e' || c == 'E' ||
402 c == '+' || c == '-');
403 chars[i++] = char(c);
406 *dp = convert(chars.begin(), i);
407 return true;
410 namespace js {
412 template bool GetDecimal(const char16_t* start, const char16_t* end,
413 double* dp);
415 template bool GetDecimal(const Latin1Char* start, const Latin1Char* end,
416 double* dp);
418 template <>
419 bool GetDecimal<Utf8Unit>(const Utf8Unit* start, const Utf8Unit* end,
420 double* dp) {
421 return GetDecimal(Utf8AsUnsignedChars(start), Utf8AsUnsignedChars(end), dp);
424 } // namespace js
426 static bool num_parseFloat(JSContext* cx, unsigned argc, Value* vp) {
427 CallArgs args = CallArgsFromVp(argc, vp);
429 if (args.length() == 0) {
430 args.rval().setNaN();
431 return true;
434 if (args[0].isNumber()) {
435 // ToString(-0) is "0", handle it accordingly.
436 if (args[0].isDouble() && args[0].toDouble() == 0.0) {
437 args.rval().setInt32(0);
438 } else {
439 args.rval().set(args[0]);
441 return true;
444 JSString* str = ToString<CanGC>(cx, args[0]);
445 if (!str) {
446 return false;
449 if (str->hasIndexValue()) {
450 args.rval().setNumber(str->getIndexValue());
451 return true;
454 JSLinearString* linear = str->ensureLinear(cx);
455 if (!linear) {
456 return false;
459 double d;
460 AutoCheckCannotGC nogc;
461 if (linear->hasLatin1Chars()) {
462 const Latin1Char* begin = linear->latin1Chars(nogc);
463 const Latin1Char* end;
464 d = js_strtod(begin, begin + linear->length(), &end);
465 if (end == begin) {
466 d = GenericNaN();
468 } else {
469 const char16_t* begin = linear->twoByteChars(nogc);
470 const char16_t* end;
471 d = js_strtod(begin, begin + linear->length(), &end);
472 if (end == begin) {
473 d = GenericNaN();
477 args.rval().setDouble(d);
478 return true;
481 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
482 // 19.2.5 parseInt ( string, radix )
483 template <typename CharT>
484 static bool ParseIntImpl(JSContext* cx, const CharT* chars, size_t length,
485 bool stripPrefix, int32_t radix, double* res) {
486 // Step 2.
487 const CharT* end = chars + length;
488 const CharT* s = SkipSpace(chars, end);
490 MOZ_ASSERT(chars <= s);
491 MOZ_ASSERT(s <= end);
493 // Steps 3-4.
494 bool negative = (s != end && s[0] == '-');
496 // Step 5. */
497 if (s != end && (s[0] == '-' || s[0] == '+')) {
498 s++;
501 // Step 10.
502 if (stripPrefix) {
503 if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
504 s += 2;
505 radix = 16;
509 // Steps 11-15.
510 const CharT* actualEnd;
511 double d;
512 if (!js::GetPrefixInteger(s, end, radix, IntegerSeparatorHandling::None,
513 &actualEnd, &d)) {
514 ReportOutOfMemory(cx);
515 return false;
518 if (s == actualEnd) {
519 *res = GenericNaN();
520 } else {
521 *res = negative ? -d : d;
523 return true;
526 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
527 // 19.2.5 parseInt ( string, radix )
528 bool js::NumberParseInt(JSContext* cx, HandleString str, int32_t radix,
529 MutableHandleValue result) {
530 // Step 7.
531 bool stripPrefix = true;
533 // Steps 8-9.
534 if (radix != 0) {
535 if (radix < 2 || radix > 36) {
536 result.setNaN();
537 return true;
540 if (radix != 16) {
541 stripPrefix = false;
543 } else {
544 radix = 10;
546 MOZ_ASSERT(2 <= radix && radix <= 36);
548 JSLinearString* linear = str->ensureLinear(cx);
549 if (!linear) {
550 return false;
553 // Steps 2-5, 10-16.
554 AutoCheckCannotGC nogc;
555 size_t length = linear->length();
556 double number;
557 if (linear->hasLatin1Chars()) {
558 if (!ParseIntImpl(cx, linear->latin1Chars(nogc), length, stripPrefix, radix,
559 &number)) {
560 return false;
562 } else {
563 if (!ParseIntImpl(cx, linear->twoByteChars(nogc), length, stripPrefix,
564 radix, &number)) {
565 return false;
569 result.setNumber(number);
570 return true;
573 // ES2023 draft rev 053d34c87b14d9234d6f7f45bd61074b72ca9d69
574 // 19.2.5 parseInt ( string, radix )
575 static bool num_parseInt(JSContext* cx, unsigned argc, Value* vp) {
576 CallArgs args = CallArgsFromVp(argc, vp);
578 /* Fast paths and exceptional cases. */
579 if (args.length() == 0) {
580 args.rval().setNaN();
581 return true;
584 if (args.length() == 1 || (args[1].isInt32() && (args[1].toInt32() == 0 ||
585 args[1].toInt32() == 10))) {
586 if (args[0].isInt32()) {
587 args.rval().set(args[0]);
588 return true;
592 * Step 1 is |inputString = ToString(string)|. When string >=
593 * 1e21, ToString(string) is in the form "NeM". 'e' marks the end of
594 * the word, which would mean the result of parseInt(string) should be |N|.
596 * To preserve this behaviour, we can't use the fast-path when string >=
597 * 1e21, or else the result would be |NeM|.
599 * The same goes for values smaller than 1.0e-6, because the string would be
600 * in the form of "Ne-M".
602 if (args[0].isDouble()) {
603 double d = args[0].toDouble();
604 if (DOUBLE_DECIMAL_IN_SHORTEST_LOW <= d &&
605 d < DOUBLE_DECIMAL_IN_SHORTEST_HIGH) {
606 args.rval().setNumber(floor(d));
607 return true;
609 if (-DOUBLE_DECIMAL_IN_SHORTEST_HIGH < d &&
610 d <= -DOUBLE_DECIMAL_IN_SHORTEST_LOW) {
611 args.rval().setNumber(-floor(-d));
612 return true;
614 if (d == 0.0) {
615 args.rval().setInt32(0);
616 return true;
620 if (args[0].isString()) {
621 JSString* str = args[0].toString();
622 if (str->hasIndexValue()) {
623 args.rval().setNumber(str->getIndexValue());
624 return true;
629 // Step 1.
630 RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
631 if (!inputString) {
632 return false;
635 // Step 6.
636 int32_t radix = 0;
637 if (args.hasDefined(1)) {
638 if (!ToInt32(cx, args[1], &radix)) {
639 return false;
643 // Steps 2-5, 7-16.
644 return NumberParseInt(cx, inputString, radix, args.rval());
647 static const JSFunctionSpec number_functions[] = {
648 JS_SELF_HOSTED_FN("isNaN", "Global_isNaN", 1, JSPROP_RESOLVING),
649 JS_SELF_HOSTED_FN("isFinite", "Global_isFinite", 1, JSPROP_RESOLVING),
650 JS_FS_END};
652 const JSClass NumberObject::class_ = {
653 "Number",
654 JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
655 JS_NULL_CLASS_OPS, &NumberObject::classSpec_};
657 static bool Number(JSContext* cx, unsigned argc, Value* vp) {
658 CallArgs args = CallArgsFromVp(argc, vp);
660 if (args.length() > 0) {
661 // BigInt proposal section 6.2, steps 2a-c.
662 if (!ToNumeric(cx, args[0])) {
663 return false;
665 if (args[0].isBigInt()) {
666 args[0].setNumber(BigInt::numberValue(args[0].toBigInt()));
668 MOZ_ASSERT(args[0].isNumber());
671 if (!args.isConstructing()) {
672 if (args.length() > 0) {
673 args.rval().set(args[0]);
674 } else {
675 args.rval().setInt32(0);
677 return true;
680 RootedObject proto(cx);
681 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Number, &proto)) {
682 return false;
685 double d = args.length() > 0 ? args[0].toNumber() : 0;
686 JSObject* obj = NumberObject::create(cx, d, proto);
687 if (!obj) {
688 return false;
690 args.rval().setObject(*obj);
691 return true;
694 // ES2020 draft rev e08b018785606bc6465a0456a79604b149007932
695 // 20.1.3 Properties of the Number Prototype Object, thisNumberValue.
696 MOZ_ALWAYS_INLINE
697 static bool ThisNumberValue(JSContext* cx, const CallArgs& args,
698 const char* methodName, double* number) {
699 HandleValue thisv = args.thisv();
701 // Step 1.
702 if (thisv.isNumber()) {
703 *number = thisv.toNumber();
704 return true;
707 // Steps 2-3.
708 auto* obj = UnwrapAndTypeCheckThis<NumberObject>(cx, args, methodName);
709 if (!obj) {
710 return false;
713 *number = obj->unbox();
714 return true;
717 // On-off helper function for the self-hosted Number_toLocaleString method.
718 // This only exists to produce an error message with the right method name.
719 bool js::ThisNumberValueForToLocaleString(JSContext* cx, unsigned argc,
720 Value* vp) {
721 CallArgs args = CallArgsFromVp(argc, vp);
723 double d;
724 if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
725 return false;
728 args.rval().setNumber(d);
729 return true;
732 static bool num_toSource(JSContext* cx, unsigned argc, Value* vp) {
733 CallArgs args = CallArgsFromVp(argc, vp);
735 double d;
736 if (!ThisNumberValue(cx, args, "toSource", &d)) {
737 return false;
740 JSStringBuilder sb(cx);
741 if (!sb.append("(new Number(") ||
742 !NumberValueToStringBuffer(NumberValue(d), sb) || !sb.append("))")) {
743 return false;
746 JSString* str = sb.finishString();
747 if (!str) {
748 return false;
750 args.rval().setString(str);
751 return true;
754 // Subtract one from DTOSTR_STANDARD_BUFFER_SIZE to exclude the null-character.
755 static_assert(
756 double_conversion::DoubleToStringConverter::kMaxCharsEcmaScriptShortest ==
757 DTOSTR_STANDARD_BUFFER_SIZE - 1,
758 "double_conversion and dtoa both agree how large the longest string "
759 "can be");
761 static_assert(DTOSTR_STANDARD_BUFFER_SIZE <= JS::MaximumNumberToStringLength,
762 "MaximumNumberToStringLength is large enough to hold the longest "
763 "string produced by a conversion");
765 MOZ_ALWAYS_INLINE
766 static JSLinearString* LookupDtoaCache(JSContext* cx, double d) {
767 if (Realm* realm = cx->realm()) {
768 if (JSLinearString* str = realm->dtoaCache.lookup(10, d)) {
769 return str;
773 return nullptr;
776 MOZ_ALWAYS_INLINE
777 static void CacheNumber(JSContext* cx, double d, JSLinearString* str) {
778 if (Realm* realm = cx->realm()) {
779 realm->dtoaCache.cache(10, d, str);
783 MOZ_ALWAYS_INLINE
784 static JSLinearString* LookupInt32ToString(JSContext* cx, int32_t si) {
785 if (si >= 0 && StaticStrings::hasInt(si)) {
786 return cx->staticStrings().getInt(si);
789 return LookupDtoaCache(cx, si);
792 template <typename T>
793 MOZ_ALWAYS_INLINE static T* BackfillInt32InBuffer(int32_t si, T* buffer,
794 size_t size, size_t* length) {
795 uint32_t ui = Abs(si);
796 MOZ_ASSERT_IF(si == INT32_MIN, ui == uint32_t(INT32_MAX) + 1);
798 RangedPtr<T> end(buffer + size - 1, buffer, size);
799 *end = '\0';
800 RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
801 if (si < 0) {
802 *--start = '-';
805 *length = end - start;
806 return start.get();
809 template <AllowGC allowGC>
810 JSLinearString* js::Int32ToString(JSContext* cx, int32_t si) {
811 return js::Int32ToStringWithHeap<allowGC>(cx, si, gc::Heap::Default);
813 template JSLinearString* js::Int32ToString<CanGC>(JSContext* cx, int32_t si);
814 template JSLinearString* js::Int32ToString<NoGC>(JSContext* cx, int32_t si);
816 template <AllowGC allowGC>
817 JSLinearString* js::Int32ToStringWithHeap(JSContext* cx, int32_t si,
818 gc::Heap heap) {
819 if (JSLinearString* str = LookupInt32ToString(cx, si)) {
820 return str;
823 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
824 size_t length;
825 Latin1Char* start =
826 BackfillInt32InBuffer(si, buffer, std::size(buffer), &length);
828 mozilla::Range<const Latin1Char> chars(start, length);
829 JSInlineString* str = NewInlineString<allowGC>(cx, chars, heap);
830 if (!str) {
831 return nullptr;
833 if (si >= 0) {
834 str->maybeInitializeIndexValue(si);
837 CacheNumber(cx, si, str);
838 return str;
840 template JSLinearString* js::Int32ToStringWithHeap<CanGC>(JSContext* cx,
841 int32_t si,
842 gc::Heap heap);
843 template JSLinearString* js::Int32ToStringWithHeap<NoGC>(JSContext* cx,
844 int32_t si,
845 gc::Heap heap);
847 JSLinearString* js::Int32ToStringPure(JSContext* cx, int32_t si) {
848 AutoUnsafeCallWithABI unsafe;
849 return Int32ToString<NoGC>(cx, si);
852 JSAtom* js::Int32ToAtom(JSContext* cx, int32_t si) {
853 if (JSLinearString* str = LookupInt32ToString(cx, si)) {
854 return js::AtomizeString(cx, str);
857 char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
858 size_t length;
859 char* start = BackfillInt32InBuffer(
860 si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
862 Maybe<uint32_t> indexValue;
863 if (si >= 0) {
864 indexValue.emplace(si);
867 JSAtom* atom = Atomize(cx, start, length, indexValue);
868 if (!atom) {
869 return nullptr;
872 CacheNumber(cx, si, atom);
873 return atom;
876 frontend::TaggedParserAtomIndex js::Int32ToParserAtom(
877 FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, int32_t si) {
878 char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
879 size_t length;
880 char* start = BackfillInt32InBuffer(
881 si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
883 Maybe<uint32_t> indexValue;
884 if (si >= 0) {
885 indexValue.emplace(si);
888 return parserAtoms.internAscii(fc, start, length);
891 /* Returns a non-nullptr pointer to inside `buf`. */
892 template <typename T>
893 static char* Int32ToCStringWithBase(mozilla::Range<char> buf, T i, size_t* len,
894 int base) {
895 uint32_t u;
896 if constexpr (std::is_signed_v<T>) {
897 u = Abs(i);
898 } else {
899 u = i;
902 RangedPtr<char> cp = buf.end() - 1;
904 char* end = cp.get();
905 *cp = '\0';
907 /* Build the string from behind. */
908 switch (base) {
909 case 10:
910 cp = BackfillIndexInCharBuffer(u, cp);
911 break;
912 case 16:
913 do {
914 unsigned newu = u / 16;
915 *--cp = "0123456789abcdef"[u - newu * 16];
916 u = newu;
917 } while (u != 0);
918 break;
919 default:
920 MOZ_ASSERT(base >= 2 && base <= 36);
921 do {
922 unsigned newu = u / base;
923 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
924 u = newu;
925 } while (u != 0);
926 break;
928 if constexpr (std::is_signed_v<T>) {
929 if (i < 0) {
930 *--cp = '-';
934 *len = end - cp.get();
935 return cp.get();
938 /* Returns a non-nullptr pointer to inside `out`. */
939 template <typename T, size_t Length>
940 static char* Int32ToCStringWithBase(char (&out)[Length], T i, size_t* len,
941 int base) {
942 // The buffer needs to be large enough to hold the largest number, including
943 // the sign and the terminating null-character.
944 static_assert(std::numeric_limits<T>::digits + (2 * std::is_signed_v<T>) <
945 Length);
947 mozilla::Range<char> buf(out, Length);
948 return Int32ToCStringWithBase(buf, i, len, base);
951 /* Returns a non-nullptr pointer to inside `out`. */
952 template <typename T, size_t Base, size_t Length>
953 static char* Int32ToCString(char (&out)[Length], T i, size_t* len) {
954 // The buffer needs to be large enough to hold the largest number, including
955 // the sign and the terminating null-character.
956 if constexpr (Base == 10) {
957 static_assert(std::numeric_limits<T>::digits10 + 1 + std::is_signed_v<T> <
958 Length);
959 } else {
960 // Compute digits16 analog to std::numeric_limits::digits10, which is
961 // defined as |std::numeric_limits::digits * std::log10(2)| for integer
962 // types.
963 // Note: log16(2) is 1/4.
964 static_assert(Base == 16);
965 static_assert(((std::numeric_limits<T>::digits + std::is_signed_v<T>) / 4 +
966 std::is_signed_v<T>) < Length);
969 mozilla::Range<char> buf(out, Length);
970 return Int32ToCStringWithBase(buf, i, len, Base);
973 /* Returns a non-nullptr pointer to inside `cbuf`. */
974 template <typename T, size_t Base = 10>
975 static char* Int32ToCString(ToCStringBuf* cbuf, T i, size_t* len) {
976 return Int32ToCString<T, Base>(cbuf->sbuf, i, len);
979 /* Returns a non-nullptr pointer to inside `cbuf`. */
980 template <typename T, size_t Base = 10>
981 static char* Int32ToCString(Int32ToCStringBuf* cbuf, T i, size_t* len) {
982 return Int32ToCString<T, Base>(cbuf->sbuf, i, len);
985 template <AllowGC allowGC>
986 static JSString* NumberToStringWithBase(JSContext* cx, double d, int base);
988 static bool num_toString(JSContext* cx, unsigned argc, Value* vp) {
989 CallArgs args = CallArgsFromVp(argc, vp);
991 double d;
992 if (!ThisNumberValue(cx, args, "toString", &d)) {
993 return false;
996 int32_t base = 10;
997 if (args.hasDefined(0)) {
998 double d2;
999 if (!ToInteger(cx, args[0], &d2)) {
1000 return false;
1003 if (d2 < 2 || d2 > 36) {
1004 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
1005 return false;
1008 base = int32_t(d2);
1010 JSString* str = NumberToStringWithBase<CanGC>(cx, d, base);
1011 if (!str) {
1012 return false;
1014 args.rval().setString(str);
1015 return true;
1018 #if !JS_HAS_INTL_API
1019 static bool num_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
1020 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype",
1021 "toLocaleString");
1022 CallArgs args = CallArgsFromVp(argc, vp);
1024 double d;
1025 if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
1026 return false;
1029 RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
1030 if (!str) {
1031 return false;
1035 * Create the string, move back to bytes to make string twiddling
1036 * a bit easier and so we can insert platform charset seperators.
1038 UniqueChars numBytes = EncodeAscii(cx, str);
1039 if (!numBytes) {
1040 return false;
1042 const char* num = numBytes.get();
1043 if (!num) {
1044 return false;
1048 * Find the first non-integer value, whether it be a letter as in
1049 * 'Infinity', a decimal point, or an 'e' from exponential notation.
1051 const char* nint = num;
1052 if (*nint == '-') {
1053 nint++;
1055 while (*nint >= '0' && *nint <= '9') {
1056 nint++;
1058 int digits = nint - num;
1059 const char* end = num + digits;
1060 if (!digits) {
1061 args.rval().setString(str);
1062 return true;
1065 JSRuntime* rt = cx->runtime();
1066 size_t thousandsLength = strlen(rt->thousandsSeparator);
1067 size_t decimalLength = strlen(rt->decimalSeparator);
1069 /* Figure out how long resulting string will be. */
1070 int buflen = strlen(num);
1071 if (*nint == '.') {
1072 buflen += decimalLength - 1; /* -1 to account for existing '.' */
1075 const char* numGrouping;
1076 const char* tmpGroup;
1077 numGrouping = tmpGroup = rt->numGrouping;
1078 int remainder = digits;
1079 if (*num == '-') {
1080 remainder--;
1083 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
1084 if (*tmpGroup >= remainder) {
1085 break;
1087 buflen += thousandsLength;
1088 remainder -= *tmpGroup;
1089 tmpGroup++;
1092 int nrepeat;
1093 if (*tmpGroup == '\0' && *numGrouping != '\0') {
1094 nrepeat = (remainder - 1) / tmpGroup[-1];
1095 buflen += thousandsLength * nrepeat;
1096 remainder -= nrepeat * tmpGroup[-1];
1097 } else {
1098 nrepeat = 0;
1100 tmpGroup--;
1102 char* buf = cx->pod_malloc<char>(buflen + 1);
1103 if (!buf) {
1104 return false;
1107 char* tmpDest = buf;
1108 const char* tmpSrc = num;
1110 while (*tmpSrc == '-' || remainder--) {
1111 MOZ_ASSERT(tmpDest - buf < buflen);
1112 *tmpDest++ = *tmpSrc++;
1114 while (tmpSrc < end) {
1115 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
1116 strcpy(tmpDest, rt->thousandsSeparator);
1117 tmpDest += thousandsLength;
1118 MOZ_ASSERT(tmpDest - buf + *tmpGroup <= buflen);
1119 js_memcpy(tmpDest, tmpSrc, *tmpGroup);
1120 tmpDest += *tmpGroup;
1121 tmpSrc += *tmpGroup;
1122 if (--nrepeat < 0) {
1123 tmpGroup--;
1127 if (*nint == '.') {
1128 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(decimalLength) <= buflen);
1129 strcpy(tmpDest, rt->decimalSeparator);
1130 tmpDest += decimalLength;
1131 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
1132 strcpy(tmpDest, nint + 1);
1133 } else {
1134 MOZ_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
1135 strcpy(tmpDest, nint);
1138 if (cx->runtime()->localeCallbacks &&
1139 cx->runtime()->localeCallbacks->localeToUnicode) {
1140 Rooted<Value> v(cx, StringValue(str));
1141 bool ok = !!cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, &v);
1142 if (ok) {
1143 args.rval().set(v);
1145 js_free(buf);
1146 return ok;
1149 str = NewStringCopyN<CanGC>(cx, buf, buflen);
1150 js_free(buf);
1151 if (!str) {
1152 return false;
1155 args.rval().setString(str);
1156 return true;
1158 #endif /* !JS_HAS_INTL_API */
1160 bool js::num_valueOf(JSContext* cx, unsigned argc, Value* vp) {
1161 CallArgs args = CallArgsFromVp(argc, vp);
1163 double d;
1164 if (!ThisNumberValue(cx, args, "valueOf", &d)) {
1165 return false;
1168 args.rval().setNumber(d);
1169 return true;
1172 static const unsigned MAX_PRECISION = 100;
1174 static bool ComputePrecisionInRange(JSContext* cx, int minPrecision,
1175 int maxPrecision, double prec,
1176 int* precision) {
1177 if (minPrecision <= prec && prec <= maxPrecision) {
1178 *precision = int(prec);
1179 return true;
1182 ToCStringBuf cbuf;
1183 char* numStr = NumberToCString(&cbuf, prec);
1184 MOZ_ASSERT(numStr);
1185 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PRECISION_RANGE,
1186 numStr);
1187 return false;
1190 static constexpr size_t DoubleToStrResultBufSize = 128;
1192 template <typename Op>
1193 [[nodiscard]] static bool DoubleToStrResult(JSContext* cx, const CallArgs& args,
1194 Op op) {
1195 char buf[DoubleToStrResultBufSize];
1197 const auto& converter =
1198 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1199 double_conversion::StringBuilder builder(buf, sizeof(buf));
1201 bool ok = op(converter, builder);
1202 MOZ_RELEASE_ASSERT(ok);
1204 size_t numStrLen = builder.position();
1205 const char* numStr = builder.Finalize();
1206 MOZ_ASSERT(numStr == buf);
1207 MOZ_ASSERT(numStrLen == strlen(numStr));
1209 JSString* str = NewStringCopyN<CanGC>(cx, numStr, numStrLen);
1210 if (!str) {
1211 return false;
1214 args.rval().setString(str);
1215 return true;
1218 // ES 2021 draft 21.1.3.3.
1219 static bool num_toFixed(JSContext* cx, unsigned argc, Value* vp) {
1220 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype", "toFixed");
1221 CallArgs args = CallArgsFromVp(argc, vp);
1223 // Step 1.
1224 double d;
1225 if (!ThisNumberValue(cx, args, "toFixed", &d)) {
1226 return false;
1229 // Steps 2-5.
1230 int precision;
1231 if (args.length() == 0) {
1232 precision = 0;
1233 } else {
1234 double prec = 0;
1235 if (!ToInteger(cx, args[0], &prec)) {
1236 return false;
1239 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
1240 return false;
1244 // Step 6.
1245 if (std::isnan(d)) {
1246 args.rval().setString(cx->names().NaN);
1247 return true;
1249 if (std::isinf(d)) {
1250 if (d > 0) {
1251 args.rval().setString(cx->names().Infinity);
1252 return true;
1255 args.rval().setString(cx->names().NegativeInfinity_);
1256 return true;
1259 // Steps 7-10 for very large numbers.
1260 if (d <= -1e21 || d >= 1e+21) {
1261 JSString* s = NumberToString<CanGC>(cx, d);
1262 if (!s) {
1263 return false;
1266 args.rval().setString(s);
1267 return true;
1270 // Steps 7-12.
1272 // DoubleToStringConverter::ToFixed is documented as requiring a buffer size
1273 // of:
1275 // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint + 1
1276 // (one additional character for the sign, one for the decimal point,
1277 // and one for the null terminator)
1279 // We already ensured there are at most 21 digits before the point, and
1280 // MAX_PRECISION digits after the point.
1281 static_assert(1 + 21 + 1 + MAX_PRECISION + 1 <= DoubleToStrResultBufSize);
1283 // The double-conversion library by default has a kMaxFixedDigitsAfterPoint of
1284 // 60. Assert our modified version supports at least MAX_PRECISION (100).
1285 using DToSConverter = double_conversion::DoubleToStringConverter;
1286 static_assert(DToSConverter::kMaxFixedDigitsAfterPoint >= MAX_PRECISION);
1288 return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
1289 return converter.ToFixed(d, precision, &builder);
1293 // ES 2021 draft 21.1.3.2.
1294 static bool num_toExponential(JSContext* cx, unsigned argc, Value* vp) {
1295 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype",
1296 "toExponential");
1297 CallArgs args = CallArgsFromVp(argc, vp);
1299 // Step 1.
1300 double d;
1301 if (!ThisNumberValue(cx, args, "toExponential", &d)) {
1302 return false;
1305 // Step 2.
1306 double prec = 0;
1307 if (args.hasDefined(0)) {
1308 if (!ToInteger(cx, args[0], &prec)) {
1309 return false;
1313 // Step 3.
1314 MOZ_ASSERT_IF(!args.hasDefined(0), prec == 0);
1316 // Step 4.
1317 if (std::isnan(d)) {
1318 args.rval().setString(cx->names().NaN);
1319 return true;
1321 if (std::isinf(d)) {
1322 if (d > 0) {
1323 args.rval().setString(cx->names().Infinity);
1324 return true;
1327 args.rval().setString(cx->names().NegativeInfinity_);
1328 return true;
1331 // Step 5.
1332 int precision = 0;
1333 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
1334 return false;
1337 // Steps 6-15.
1339 // DoubleToStringConverter::ToExponential is documented as adding at most 8
1340 // characters on top of the requested digits: "the sign, the digit before the
1341 // decimal point, the decimal point, the exponent character, the exponent's
1342 // sign, and at most 3 exponent digits". In addition, the buffer must be able
1343 // to hold the trailing '\0' character.
1344 static_assert(MAX_PRECISION + 8 + 1 <= DoubleToStrResultBufSize);
1346 return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
1347 int requestedDigits = args.hasDefined(0) ? precision : -1;
1348 return converter.ToExponential(d, requestedDigits, &builder);
1352 // ES 2021 draft 21.1.3.5.
1353 static bool num_toPrecision(JSContext* cx, unsigned argc, Value* vp) {
1354 AutoJSMethodProfilerEntry pseudoFrame(cx, "Number.prototype", "toPrecision");
1355 CallArgs args = CallArgsFromVp(argc, vp);
1357 // Step 1.
1358 double d;
1359 if (!ThisNumberValue(cx, args, "toPrecision", &d)) {
1360 return false;
1363 // Step 2.
1364 if (!args.hasDefined(0)) {
1365 JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10);
1366 if (!str) {
1367 return false;
1369 args.rval().setString(str);
1370 return true;
1373 // Step 3.
1374 double prec = 0;
1375 if (!ToInteger(cx, args[0], &prec)) {
1376 return false;
1379 // Step 4.
1380 if (std::isnan(d)) {
1381 args.rval().setString(cx->names().NaN);
1382 return true;
1384 if (std::isinf(d)) {
1385 if (d > 0) {
1386 args.rval().setString(cx->names().Infinity);
1387 return true;
1390 args.rval().setString(cx->names().NegativeInfinity_);
1391 return true;
1394 // Step 5.
1395 int precision = 0;
1396 if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, prec, &precision)) {
1397 return false;
1400 // Steps 6-14.
1402 // DoubleToStringConverter::ToPrecision is documented as adding at most 7
1403 // characters on top of the requested digits: "the sign, the decimal point,
1404 // the exponent character, the exponent's sign, and at most 3 exponent
1405 // digits". In addition, the buffer must be able to hold the trailing '\0'
1406 // character.
1407 static_assert(MAX_PRECISION + 7 + 1 <= DoubleToStrResultBufSize);
1409 return DoubleToStrResult(cx, args, [&](auto& converter, auto& builder) {
1410 return converter.ToPrecision(d, precision, &builder);
1414 static const JSFunctionSpec number_methods[] = {
1415 JS_FN("toSource", num_toSource, 0, 0),
1416 JS_INLINABLE_FN("toString", num_toString, 1, 0, NumberToString),
1417 #if JS_HAS_INTL_API
1418 JS_SELF_HOSTED_FN("toLocaleString", "Number_toLocaleString", 0, 0),
1419 #else
1420 JS_FN("toLocaleString", num_toLocaleString, 0, 0),
1421 #endif
1422 JS_FN("valueOf", num_valueOf, 0, 0),
1423 JS_FN("toFixed", num_toFixed, 1, 0),
1424 JS_FN("toExponential", num_toExponential, 1, 0),
1425 JS_FN("toPrecision", num_toPrecision, 1, 0),
1426 JS_FS_END};
1428 bool js::IsInteger(double d) {
1429 return std::isfinite(d) && JS::ToInteger(d) == d;
1432 static const JSFunctionSpec number_static_methods[] = {
1433 JS_SELF_HOSTED_FN("isFinite", "Number_isFinite", 1, 0),
1434 JS_SELF_HOSTED_FN("isInteger", "Number_isInteger", 1, 0),
1435 JS_SELF_HOSTED_FN("isNaN", "Number_isNaN", 1, 0),
1436 JS_SELF_HOSTED_FN("isSafeInteger", "Number_isSafeInteger", 1, 0),
1437 JS_FS_END};
1439 static const JSPropertySpec number_static_properties[] = {
1440 JS_DOUBLE_PS("POSITIVE_INFINITY", mozilla::PositiveInfinity<double>(),
1441 JSPROP_READONLY | JSPROP_PERMANENT),
1442 JS_DOUBLE_PS("NEGATIVE_INFINITY", mozilla::NegativeInfinity<double>(),
1443 JSPROP_READONLY | JSPROP_PERMANENT),
1444 JS_DOUBLE_PS("MAX_VALUE", 1.7976931348623157E+308,
1445 JSPROP_READONLY | JSPROP_PERMANENT),
1446 JS_DOUBLE_PS("MIN_VALUE", MinNumberValue<double>(),
1447 JSPROP_READONLY | JSPROP_PERMANENT),
1448 /* ES6 (April 2014 draft) 20.1.2.6 */
1449 JS_DOUBLE_PS("MAX_SAFE_INTEGER", 9007199254740991,
1450 JSPROP_READONLY | JSPROP_PERMANENT),
1451 /* ES6 (April 2014 draft) 20.1.2.10 */
1452 JS_DOUBLE_PS("MIN_SAFE_INTEGER", -9007199254740991,
1453 JSPROP_READONLY | JSPROP_PERMANENT),
1454 /* ES6 (May 2013 draft) 15.7.3.7 */
1455 JS_DOUBLE_PS("EPSILON", 2.2204460492503130808472633361816e-16,
1456 JSPROP_READONLY | JSPROP_PERMANENT),
1457 JS_PS_END};
1459 bool js::InitRuntimeNumberState(JSRuntime* rt) {
1460 // XXX If JS_HAS_INTL_API becomes true all the time at some point,
1461 // js::InitRuntimeNumberState is no longer fallible, and we should
1462 // change its return type.
1463 #if !JS_HAS_INTL_API
1464 /* Copy locale-specific separators into the runtime strings. */
1465 const char* thousandsSeparator;
1466 const char* decimalPoint;
1467 const char* grouping;
1468 # ifdef HAVE_LOCALECONV
1469 struct lconv* locale = localeconv();
1470 thousandsSeparator = locale->thousands_sep;
1471 decimalPoint = locale->decimal_point;
1472 grouping = locale->grouping;
1473 # else
1474 thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
1475 decimalPoint = getenv("LOCALE_DECIMAL_POINT");
1476 grouping = getenv("LOCALE_GROUPING");
1477 # endif
1478 if (!thousandsSeparator) {
1479 thousandsSeparator = "'";
1481 if (!decimalPoint) {
1482 decimalPoint = ".";
1484 if (!grouping) {
1485 grouping = "\3\0";
1489 * We use single malloc to get the memory for all separator and grouping
1490 * strings.
1492 size_t thousandsSeparatorSize = strlen(thousandsSeparator) + 1;
1493 size_t decimalPointSize = strlen(decimalPoint) + 1;
1494 size_t groupingSize = strlen(grouping) + 1;
1496 char* storage = js_pod_malloc<char>(thousandsSeparatorSize +
1497 decimalPointSize + groupingSize);
1498 if (!storage) {
1499 return false;
1502 js_memcpy(storage, thousandsSeparator, thousandsSeparatorSize);
1503 rt->thousandsSeparator = storage;
1504 storage += thousandsSeparatorSize;
1506 js_memcpy(storage, decimalPoint, decimalPointSize);
1507 rt->decimalSeparator = storage;
1508 storage += decimalPointSize;
1510 js_memcpy(storage, grouping, groupingSize);
1511 rt->numGrouping = grouping;
1512 #endif /* !JS_HAS_INTL_API */
1513 return true;
1516 void js::FinishRuntimeNumberState(JSRuntime* rt) {
1517 #if !JS_HAS_INTL_API
1519 * The free also releases the memory for decimalSeparator and numGrouping
1520 * strings.
1522 char* storage = const_cast<char*>(rt->thousandsSeparator.ref());
1523 js_free(storage);
1524 #endif // !JS_HAS_INTL_API
1527 JSObject* NumberObject::createPrototype(JSContext* cx, JSProtoKey key) {
1528 NumberObject* numberProto =
1529 GlobalObject::createBlankPrototype<NumberObject>(cx, cx->global());
1530 if (!numberProto) {
1531 return nullptr;
1533 numberProto->setPrimitiveValue(0);
1534 return numberProto;
1537 static bool NumberClassFinish(JSContext* cx, HandleObject ctor,
1538 HandleObject proto) {
1539 Handle<GlobalObject*> global = cx->global();
1541 if (!JS_DefineFunctions(cx, global, number_functions)) {
1542 return false;
1545 // Number.parseInt should be the same function object as global parseInt.
1546 RootedId parseIntId(cx, NameToId(cx->names().parseInt));
1547 JSFunction* parseInt =
1548 DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING);
1549 if (!parseInt) {
1550 return false;
1552 parseInt->setJitInfo(&jit::JitInfo_NumberParseInt);
1554 RootedValue parseIntValue(cx, ObjectValue(*parseInt));
1555 if (!DefineDataProperty(cx, ctor, parseIntId, parseIntValue, 0)) {
1556 return false;
1559 // Number.parseFloat should be the same function object as global
1560 // parseFloat.
1561 RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
1562 JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
1563 num_parseFloat, 1, JSPROP_RESOLVING);
1564 if (!parseFloat) {
1565 return false;
1567 RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
1568 if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) {
1569 return false;
1572 RootedValue valueNaN(cx, JS::NaNValue());
1573 RootedValue valueInfinity(cx, JS::InfinityValue());
1575 if (!DefineDataProperty(
1576 cx, ctor, cx->names().NaN, valueNaN,
1577 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
1578 return false;
1581 // ES5 15.1.1.1, 15.1.1.2
1582 if (!NativeDefineDataProperty(
1583 cx, global, cx->names().NaN, valueNaN,
1584 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING) ||
1585 !NativeDefineDataProperty(
1586 cx, global, cx->names().Infinity, valueInfinity,
1587 JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_RESOLVING)) {
1588 return false;
1591 return true;
1594 const ClassSpec NumberObject::classSpec_ = {
1595 GenericCreateConstructor<Number, 1, gc::AllocKind::FUNCTION,
1596 &jit::JitInfo_Number>,
1597 NumberObject::createPrototype,
1598 number_static_methods,
1599 number_static_properties,
1600 number_methods,
1601 nullptr,
1602 NumberClassFinish};
1604 static char* FracNumberToCString(ToCStringBuf* cbuf, double d, size_t* len) {
1605 #ifdef DEBUG
1607 int32_t _;
1608 MOZ_ASSERT(!NumberEqualsInt32(d, &_));
1610 #endif
1613 * This is V8's implementation of the algorithm described in the
1614 * following paper:
1616 * Printing floating-point numbers quickly and accurately with integers.
1617 * Florian Loitsch, PLDI 2010.
1619 const double_conversion::DoubleToStringConverter& converter =
1620 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1621 double_conversion::StringBuilder builder(cbuf->sbuf, std::size(cbuf->sbuf));
1622 converter.ToShortest(d, &builder);
1624 *len = builder.position();
1625 return builder.Finalize();
1628 void JS::NumberToString(double d, char (&out)[MaximumNumberToStringLength]) {
1629 int32_t i;
1630 if (NumberEqualsInt32(d, &i)) {
1631 Int32ToCStringBuf cbuf;
1632 size_t len;
1633 char* loc = ::Int32ToCString(&cbuf, i, &len);
1634 memmove(out, loc, len);
1635 out[len] = '\0';
1636 } else {
1637 const double_conversion::DoubleToStringConverter& converter =
1638 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1640 double_conversion::StringBuilder builder(out, sizeof(out));
1641 converter.ToShortest(d, &builder);
1643 #ifdef DEBUG
1644 char* result =
1645 #endif
1646 builder.Finalize();
1647 MOZ_ASSERT(out == result);
1651 char* js::NumberToCString(ToCStringBuf* cbuf, double d, size_t* length) {
1652 int32_t i;
1653 size_t len;
1654 char* s = NumberEqualsInt32(d, &i) ? ::Int32ToCString(cbuf, i, &len)
1655 : FracNumberToCString(cbuf, d, &len);
1656 MOZ_ASSERT(s);
1657 if (length) {
1658 *length = len;
1660 return s;
1663 char* js::Int32ToCString(Int32ToCStringBuf* cbuf, int32_t value,
1664 size_t* length) {
1665 size_t len;
1666 char* s = ::Int32ToCString(cbuf, value, &len);
1667 MOZ_ASSERT(s);
1668 if (length) {
1669 *length = len;
1671 return s;
1674 char* js::Uint32ToCString(Int32ToCStringBuf* cbuf, uint32_t value,
1675 size_t* length) {
1676 size_t len;
1677 char* s = ::Int32ToCString(cbuf, value, &len);
1678 MOZ_ASSERT(s);
1679 if (length) {
1680 *length = len;
1682 return s;
1685 char* js::Uint32ToHexCString(Int32ToCStringBuf* cbuf, uint32_t value,
1686 size_t* length) {
1687 size_t len;
1688 char* s = ::Int32ToCString<uint32_t, 16>(cbuf, value, &len);
1689 MOZ_ASSERT(s);
1690 if (length) {
1691 *length = len;
1693 return s;
1696 template <AllowGC allowGC>
1697 static JSString* NumberToStringWithBase(JSContext* cx, double d, int base) {
1698 MOZ_ASSERT(2 <= base && base <= 36);
1700 Realm* realm = cx->realm();
1702 int32_t i;
1703 if (NumberEqualsInt32(d, &i)) {
1704 bool isBase10Int = (base == 10);
1705 if (isBase10Int) {
1706 static_assert(StaticStrings::INT_STATIC_LIMIT > 10 * 10);
1707 if (StaticStrings::hasInt(i)) {
1708 return cx->staticStrings().getInt(i);
1710 } else if (unsigned(i) < unsigned(base)) {
1711 if (i < 10) {
1712 return cx->staticStrings().getInt(i);
1714 char16_t c = 'a' + i - 10;
1715 MOZ_ASSERT(StaticStrings::hasUnit(c));
1716 return cx->staticStrings().getUnit(c);
1717 } else if (unsigned(i) < unsigned(base * base)) {
1718 static constexpr char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1719 char chars[] = {digits[i / base], digits[i % base]};
1720 JSString* str = cx->staticStrings().lookup(chars, 2);
1721 MOZ_ASSERT(str);
1722 return str;
1725 if (JSLinearString* str = realm->dtoaCache.lookup(base, d)) {
1726 return str;
1729 // Plus three to include the largest number, the sign, and the terminating
1730 // null character.
1731 constexpr size_t MaximumLength = std::numeric_limits<int32_t>::digits + 3;
1733 char buf[MaximumLength] = {};
1734 size_t numStrLen;
1735 char* numStr = Int32ToCStringWithBase(buf, i, &numStrLen, base);
1736 MOZ_ASSERT(numStrLen == strlen(numStr));
1738 JSLinearString* s = NewStringCopyN<allowGC>(cx, numStr, numStrLen);
1739 if (!s) {
1740 return nullptr;
1743 if (isBase10Int && i >= 0) {
1744 s->maybeInitializeIndexValue(i);
1747 realm->dtoaCache.cache(base, d, s);
1748 return s;
1751 if (JSLinearString* str = realm->dtoaCache.lookup(base, d)) {
1752 return str;
1755 JSLinearString* s;
1756 if (base == 10) {
1757 // We use a faster algorithm for base 10.
1758 ToCStringBuf cbuf;
1759 size_t numStrLen;
1760 char* numStr = FracNumberToCString(&cbuf, d, &numStrLen);
1761 MOZ_ASSERT(numStr);
1762 MOZ_ASSERT(numStrLen == strlen(numStr));
1764 s = NewStringCopyN<allowGC>(cx, numStr, numStrLen);
1765 if (!s) {
1766 return nullptr;
1768 } else {
1769 if (!EnsureDtoaState(cx)) {
1770 if constexpr (allowGC) {
1771 ReportOutOfMemory(cx);
1773 return nullptr;
1776 UniqueChars numStr(js_dtobasestr(cx->dtoaState, base, d));
1777 if (!numStr) {
1778 if constexpr (allowGC) {
1779 ReportOutOfMemory(cx);
1781 return nullptr;
1784 s = NewStringCopyZ<allowGC>(cx, numStr.get());
1785 if (!s) {
1786 return nullptr;
1790 realm->dtoaCache.cache(base, d, s);
1791 return s;
1794 template <AllowGC allowGC>
1795 JSString* js::NumberToString(JSContext* cx, double d) {
1796 return NumberToStringWithBase<allowGC>(cx, d, 10);
1799 template JSString* js::NumberToString<CanGC>(JSContext* cx, double d);
1801 template JSString* js::NumberToString<NoGC>(JSContext* cx, double d);
1803 JSString* js::NumberToStringPure(JSContext* cx, double d) {
1804 AutoUnsafeCallWithABI unsafe;
1805 return NumberToString<NoGC>(cx, d);
1808 JSAtom* js::NumberToAtom(JSContext* cx, double d) {
1809 int32_t si;
1810 if (NumberEqualsInt32(d, &si)) {
1811 return Int32ToAtom(cx, si);
1814 if (JSLinearString* str = LookupDtoaCache(cx, d)) {
1815 return AtomizeString(cx, str);
1818 ToCStringBuf cbuf;
1819 size_t length;
1820 char* numStr = FracNumberToCString(&cbuf, d, &length);
1821 MOZ_ASSERT(numStr);
1822 MOZ_ASSERT(std::begin(cbuf.sbuf) <= numStr && numStr < std::end(cbuf.sbuf));
1823 MOZ_ASSERT(length == strlen(numStr));
1825 JSAtom* atom = Atomize(cx, numStr, length);
1826 if (!atom) {
1827 return nullptr;
1830 CacheNumber(cx, d, atom);
1832 return atom;
1835 frontend::TaggedParserAtomIndex js::NumberToParserAtom(
1836 FrontendContext* fc, frontend::ParserAtomsTable& parserAtoms, double d) {
1837 int32_t si;
1838 if (NumberEqualsInt32(d, &si)) {
1839 return Int32ToParserAtom(fc, parserAtoms, si);
1842 ToCStringBuf cbuf;
1843 size_t length;
1844 char* numStr = FracNumberToCString(&cbuf, d, &length);
1845 MOZ_ASSERT(numStr);
1846 MOZ_ASSERT(std::begin(cbuf.sbuf) <= numStr && numStr < std::end(cbuf.sbuf));
1847 MOZ_ASSERT(length == strlen(numStr));
1849 return parserAtoms.internAscii(fc, numStr, length);
1852 JSLinearString* js::IndexToString(JSContext* cx, uint32_t index) {
1853 if (StaticStrings::hasUint(index)) {
1854 return cx->staticStrings().getUint(index);
1857 Realm* realm = cx->realm();
1858 if (JSLinearString* str = realm->dtoaCache.lookup(10, index)) {
1859 return str;
1862 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
1863 RangedPtr<Latin1Char> end(buffer + JSFatInlineString::MAX_LENGTH_LATIN1,
1864 buffer, JSFatInlineString::MAX_LENGTH_LATIN1 + 1);
1865 *end = '\0';
1866 RangedPtr<Latin1Char> start = BackfillIndexInCharBuffer(index, end);
1868 mozilla::Range<const Latin1Char> chars(start.get(), end - start);
1869 JSInlineString* str =
1870 NewInlineString<CanGC>(cx, chars, js::gc::Heap::Default);
1871 if (!str) {
1872 return nullptr;
1875 realm->dtoaCache.cache(10, index, str);
1876 return str;
1879 JSString* js::Int32ToStringWithBase(JSContext* cx, int32_t i, int32_t base) {
1880 return NumberToStringWithBase<CanGC>(cx, double(i), base);
1883 bool js::NumberValueToStringBuffer(const Value& v, StringBuffer& sb) {
1884 /* Convert to C-string. */
1885 ToCStringBuf cbuf;
1886 const char* cstr;
1887 size_t cstrlen;
1888 if (v.isInt32()) {
1889 cstr = ::Int32ToCString(&cbuf, v.toInt32(), &cstrlen);
1890 } else {
1891 cstr = NumberToCString(&cbuf, v.toDouble(), &cstrlen);
1893 MOZ_ASSERT(cstr);
1894 MOZ_ASSERT(cstrlen == strlen(cstr));
1896 MOZ_ASSERT(cstrlen < std::size(cbuf.sbuf));
1897 return sb.append(cstr, cstrlen);
1900 template <typename CharT>
1901 inline double CharToNumber(CharT c) {
1902 if ('0' <= c && c <= '9') {
1903 return c - '0';
1905 if (unicode::IsSpace(c)) {
1906 return 0.0;
1908 return GenericNaN();
1911 template <typename CharT>
1912 inline bool CharsToNonDecimalNumber(const CharT* start, const CharT* end,
1913 double* result) {
1914 MOZ_ASSERT(end - start >= 2);
1915 MOZ_ASSERT(start[0] == '0');
1917 int radix = 0;
1918 if (start[1] == 'b' || start[1] == 'B') {
1919 radix = 2;
1920 } else if (start[1] == 'o' || start[1] == 'O') {
1921 radix = 8;
1922 } else if (start[1] == 'x' || start[1] == 'X') {
1923 radix = 16;
1924 } else {
1925 return false;
1928 // It's probably a non-decimal number. Accept if there's at least one digit
1929 // after the 0b|0o|0x, and if no non-whitespace characters follow all the
1930 // digits.
1931 const CharT* endptr;
1932 double d;
1933 MOZ_ALWAYS_TRUE(GetPrefixIntegerImpl(
1934 start + 2, end, radix, IntegerSeparatorHandling::None, &endptr, &d));
1935 if (endptr == start + 2 || SkipSpace(endptr, end) != end) {
1936 *result = GenericNaN();
1937 } else {
1938 *result = d;
1940 return true;
1943 template <typename CharT>
1944 double js::CharsToNumber(const CharT* chars, size_t length) {
1945 if (length == 1) {
1946 return CharToNumber(chars[0]);
1949 const CharT* end = chars + length;
1950 const CharT* start = SkipSpace(chars, end);
1952 // ECMA doesn't allow signed non-decimal numbers (bug 273467).
1953 if (end - start >= 2 && start[0] == '0') {
1954 double d;
1955 if (CharsToNonDecimalNumber(start, end, &d)) {
1956 return d;
1961 * Note that ECMA doesn't treat a string beginning with a '0' as
1962 * an octal number here. This works because all such numbers will
1963 * be interpreted as decimal by js_strtod. Also, any hex numbers
1964 * that have made it here (which can only be negative ones) will
1965 * be treated as 0 without consuming the 'x' by js_strtod.
1967 const CharT* ep;
1968 double d = js_strtod(start, end, &ep);
1969 if (SkipSpace(ep, end) != end) {
1970 return GenericNaN();
1972 return d;
1975 template double js::CharsToNumber(const Latin1Char* chars, size_t length);
1977 template double js::CharsToNumber(const char16_t* chars, size_t length);
1979 double js::LinearStringToNumber(JSLinearString* str) {
1980 if (str->hasIndexValue()) {
1981 return str->getIndexValue();
1984 AutoCheckCannotGC nogc;
1985 return str->hasLatin1Chars()
1986 ? CharsToNumber(str->latin1Chars(nogc), str->length())
1987 : CharsToNumber(str->twoByteChars(nogc), str->length());
1990 bool js::StringToNumber(JSContext* cx, JSString* str, double* result) {
1991 JSLinearString* linearStr = str->ensureLinear(cx);
1992 if (!linearStr) {
1993 return false;
1996 *result = LinearStringToNumber(linearStr);
1997 return true;
2000 bool js::StringToNumberPure(JSContext* cx, JSString* str, double* result) {
2001 // IC Code calls this directly.
2002 AutoUnsafeCallWithABI unsafe;
2004 if (!StringToNumber(cx, str, result)) {
2005 cx->recoverFromOutOfMemory();
2006 return false;
2008 return true;
2011 JS_PUBLIC_API bool js::ToNumberSlow(JSContext* cx, HandleValue v_,
2012 double* out) {
2013 RootedValue v(cx, v_);
2014 MOZ_ASSERT(!v.isNumber());
2016 if (!v.isPrimitive()) {
2017 if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) {
2018 return false;
2021 if (v.isNumber()) {
2022 *out = v.toNumber();
2023 return true;
2026 if (v.isString()) {
2027 return StringToNumber(cx, v.toString(), out);
2029 if (v.isBoolean()) {
2030 *out = v.toBoolean() ? 1.0 : 0.0;
2031 return true;
2033 if (v.isNull()) {
2034 *out = 0.0;
2035 return true;
2037 if (v.isUndefined()) {
2038 *out = GenericNaN();
2039 return true;
2041 #ifdef ENABLE_RECORD_TUPLE
2042 if (v.isExtendedPrimitive()) {
2043 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2044 JSMSG_RECORD_TUPLE_TO_NUMBER);
2045 return false;
2047 #endif
2049 MOZ_ASSERT(v.isSymbol() || v.isBigInt());
2050 unsigned errnum = JSMSG_SYMBOL_TO_NUMBER;
2051 if (v.isBigInt()) {
2052 errnum = JSMSG_BIGINT_TO_NUMBER;
2054 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errnum);
2055 return false;
2058 // BigInt proposal section 3.1.6
2059 bool js::ToNumericSlow(JSContext* cx, MutableHandleValue vp) {
2060 MOZ_ASSERT(!vp.isNumeric());
2062 // Step 1.
2063 if (!vp.isPrimitive()) {
2064 if (!ToPrimitive(cx, JSTYPE_NUMBER, vp)) {
2065 return false;
2069 // Step 2.
2070 if (vp.isBigInt()) {
2071 return true;
2074 // Step 3.
2075 return ToNumber(cx, vp);
2079 * Convert a value to an int8_t, according to the WebIDL rules for byte
2080 * conversion. Return converted value in *out on success, false on failure.
2082 JS_PUBLIC_API bool js::ToInt8Slow(JSContext* cx, const HandleValue v,
2083 int8_t* out) {
2084 MOZ_ASSERT(!v.isInt32());
2085 double d;
2086 if (v.isDouble()) {
2087 d = v.toDouble();
2088 } else {
2089 if (!ToNumberSlow(cx, v, &d)) {
2090 return false;
2093 *out = ToInt8(d);
2094 return true;
2098 * Convert a value to an uint8_t, according to the ToUInt8() function in ES6
2099 * ECMA-262, 7.1.10. Return converted value in *out on success, false on
2100 * failure.
2102 JS_PUBLIC_API bool js::ToUint8Slow(JSContext* cx, const HandleValue v,
2103 uint8_t* out) {
2104 MOZ_ASSERT(!v.isInt32());
2105 double d;
2106 if (v.isDouble()) {
2107 d = v.toDouble();
2108 } else {
2109 if (!ToNumberSlow(cx, v, &d)) {
2110 return false;
2113 *out = ToUint8(d);
2114 return true;
2118 * Convert a value to an int16_t, according to the WebIDL rules for short
2119 * conversion. Return converted value in *out on success, false on failure.
2121 JS_PUBLIC_API bool js::ToInt16Slow(JSContext* cx, const HandleValue v,
2122 int16_t* out) {
2123 MOZ_ASSERT(!v.isInt32());
2124 double d;
2125 if (v.isDouble()) {
2126 d = v.toDouble();
2127 } else {
2128 if (!ToNumberSlow(cx, v, &d)) {
2129 return false;
2132 *out = ToInt16(d);
2133 return true;
2137 * Convert a value to an int64_t, according to the WebIDL rules for long long
2138 * conversion. Return converted value in *out on success, false on failure.
2140 JS_PUBLIC_API bool js::ToInt64Slow(JSContext* cx, const HandleValue v,
2141 int64_t* out) {
2142 MOZ_ASSERT(!v.isInt32());
2143 double d;
2144 if (v.isDouble()) {
2145 d = v.toDouble();
2146 } else {
2147 if (!ToNumberSlow(cx, v, &d)) {
2148 return false;
2151 *out = ToInt64(d);
2152 return true;
2156 * Convert a value to an uint64_t, according to the WebIDL rules for unsigned
2157 * long long conversion. Return converted value in *out on success, false on
2158 * failure.
2160 JS_PUBLIC_API bool js::ToUint64Slow(JSContext* cx, const HandleValue v,
2161 uint64_t* out) {
2162 MOZ_ASSERT(!v.isInt32());
2163 double d;
2164 if (v.isDouble()) {
2165 d = v.toDouble();
2166 } else {
2167 if (!ToNumberSlow(cx, v, &d)) {
2168 return false;
2171 *out = ToUint64(d);
2172 return true;
2175 JS_PUBLIC_API bool js::ToInt32Slow(JSContext* cx, const HandleValue v,
2176 int32_t* out) {
2177 MOZ_ASSERT(!v.isInt32());
2178 double d;
2179 if (v.isDouble()) {
2180 d = v.toDouble();
2181 } else {
2182 if (!ToNumberSlow(cx, v, &d)) {
2183 return false;
2186 *out = ToInt32(d);
2187 return true;
2190 bool js::ToInt32OrBigIntSlow(JSContext* cx, MutableHandleValue vp) {
2191 MOZ_ASSERT(!vp.isInt32());
2192 if (vp.isDouble()) {
2193 vp.setInt32(ToInt32(vp.toDouble()));
2194 return true;
2197 if (!ToNumeric(cx, vp)) {
2198 return false;
2201 if (vp.isBigInt()) {
2202 return true;
2205 vp.setInt32(ToInt32(vp.toNumber()));
2206 return true;
2209 JS_PUBLIC_API bool js::ToUint32Slow(JSContext* cx, const HandleValue v,
2210 uint32_t* out) {
2211 MOZ_ASSERT(!v.isInt32());
2212 double d;
2213 if (v.isDouble()) {
2214 d = v.toDouble();
2215 } else {
2216 if (!ToNumberSlow(cx, v, &d)) {
2217 return false;
2220 *out = ToUint32(d);
2221 return true;
2224 JS_PUBLIC_API bool js::ToUint16Slow(JSContext* cx, const HandleValue v,
2225 uint16_t* out) {
2226 MOZ_ASSERT(!v.isInt32());
2227 double d;
2228 if (v.isDouble()) {
2229 d = v.toDouble();
2230 } else if (!ToNumberSlow(cx, v, &d)) {
2231 return false;
2233 *out = ToUint16(d);
2234 return true;
2237 // ES2017 draft 7.1.17 ToIndex
2238 bool js::ToIndexSlow(JSContext* cx, JS::HandleValue v,
2239 const unsigned errorNumber, uint64_t* index) {
2240 MOZ_ASSERT_IF(v.isInt32(), v.toInt32() < 0);
2242 // Step 1.
2243 if (v.isUndefined()) {
2244 *index = 0;
2245 return true;
2248 // Step 2.a.
2249 double integerIndex;
2250 if (!ToInteger(cx, v, &integerIndex)) {
2251 return false;
2254 // Inlined version of ToLength.
2255 // 1. Already an integer.
2256 // 2. Step eliminates < 0, +0 == -0 with SameValueZero.
2257 // 3/4. Limit to <= 2^53-1, so everything above should fail.
2258 if (integerIndex < 0 || integerIndex >= DOUBLE_INTEGRAL_PRECISION_LIMIT) {
2259 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
2260 return false;
2263 // Step 3.
2264 *index = uint64_t(integerIndex);
2265 return true;
2268 template <typename CharT>
2269 double js_strtod(const CharT* begin, const CharT* end, const CharT** dEnd) {
2270 const CharT* s = SkipSpace(begin, end);
2271 size_t length = end - s;
2274 // StringToDouble can make indirect calls but can't trigger a GC.
2275 JS::AutoSuppressGCAnalysis nogc;
2277 using SToDConverter = double_conversion::StringToDoubleConverter;
2278 SToDConverter converter(SToDConverter::ALLOW_TRAILING_JUNK,
2279 /* empty_string_value = */ 0.0,
2280 /* junk_string_value = */ GenericNaN(),
2281 /* infinity_symbol = */ nullptr,
2282 /* nan_symbol = */ nullptr);
2283 int lengthInt = mozilla::AssertedCast<int>(length);
2284 double d;
2285 int processed = 0;
2286 if constexpr (std::is_same_v<CharT, char16_t>) {
2287 d = converter.StringToDouble(reinterpret_cast<const uc16*>(s), lengthInt,
2288 &processed);
2289 } else {
2290 static_assert(std::is_same_v<CharT, Latin1Char>);
2291 d = converter.StringToDouble(reinterpret_cast<const char*>(s), lengthInt,
2292 &processed);
2294 MOZ_ASSERT(processed >= 0);
2295 MOZ_ASSERT(processed <= lengthInt);
2297 if (processed > 0) {
2298 *dEnd = s + processed;
2299 return d;
2303 // Try to parse +Infinity, -Infinity or Infinity. Note that we do this here
2304 // instead of using StringToDoubleConverter's infinity_symbol because it's
2305 // faster: the code below is less generic and not on the fast path for regular
2306 // doubles.
2307 static constexpr std::string_view Infinity = "Infinity";
2308 if (length >= Infinity.length()) {
2309 const CharT* afterSign = s;
2310 bool negative = (*afterSign == '-');
2311 if (negative || *afterSign == '+') {
2312 afterSign++;
2314 MOZ_ASSERT(afterSign < end);
2315 if (*afterSign == 'I' && size_t(end - afterSign) >= Infinity.length() &&
2316 EqualChars(afterSign, Infinity.data(), Infinity.length())) {
2317 *dEnd = afterSign + Infinity.length();
2318 return negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
2322 *dEnd = begin;
2323 return 0.0;
2326 template double js_strtod(const char16_t* begin, const char16_t* end,
2327 const char16_t** dEnd);
2329 template double js_strtod(const Latin1Char* begin, const Latin1Char* end,
2330 const Latin1Char** dEnd);