Bug 1753131 - Dispatch devicechange events even without an actively capturing MediaSt...
[gecko.git] / js / src / jsnum.cpp
blob6c925c380e6c1edc8fc384b421ddb8f262bc566c
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/FloatingPoint.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/RangedPtr.h"
16 #include "mozilla/TextUtils.h"
17 #include "mozilla/Utf8.h"
19 #include <iterator>
20 #ifdef HAVE_LOCALECONV
21 # include <locale.h>
22 #endif
23 #include <math.h>
24 #include <string.h> // memmove
26 #include "jstypes.h"
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_*
34 #if !JS_HAS_INTL_API
35 # include "js/LocaleSensitive.h"
36 #endif
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"
55 using namespace js;
57 using mozilla::Abs;
58 using mozilla::AsciiAlphanumericToNumber;
59 using mozilla::IsAsciiAlphanumeric;
60 using mozilla::IsAsciiDigit;
61 using mozilla::Maybe;
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;
71 using JS::GenericNaN;
72 using JS::ToInt16;
73 using JS::ToInt32;
74 using JS::ToInt64;
75 using JS::ToInt8;
76 using JS::ToUint16;
77 using JS::ToUint32;
78 using JS::ToUint64;
79 using JS::ToUint8;
81 static bool EnsureDtoaState(JSContext* cx) {
82 if (!cx->dtoaState) {
83 cx->dtoaState = NewDtoaState();
84 if (!cx->dtoaState) {
85 return false;
88 return true;
91 template <typename CharT>
92 static inline void AssertWellPlacedNumericSeparator(const CharT* s,
93 const CharT* start,
94 const CharT* end) {
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);
115 if (!cstr) {
116 return false;
119 size_t j = 0;
120 for (size_t i = 0; i < length; i++) {
121 char c = char(start[i]);
122 if (c == '_') {
123 AssertWellPlacedNumericSeparator(start + i, start, end);
124 continue;
126 MOZ_ASSERT(IsAsciiAlphanumeric(c));
127 cstr[j++] = c;
129 cstr[j] = 0;
131 if (!EnsureDtoaState(cx)) {
132 return false;
135 char* estr;
136 *dp = js_strtod_harder(cx->dtoaState, cstr.get(), &estr);
138 return true;
141 namespace {
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 */
152 public:
153 BinaryDigitReader(int base, const CharT* start, const CharT* end)
154 : base(base),
155 digit(0),
156 digitMask(0),
157 cur(start),
158 start(start),
159 end(end) {}
161 /* Return the next binary digit from the number, or -1 if done. */
162 int nextDigit() {
163 if (digitMask == 0) {
164 if (cur == end) {
165 return -1;
168 int c = *cur++;
169 if (c == '_') {
170 AssertWellPlacedNumericSeparator(cur - 1, start, end);
171 c = *cur++;
174 MOZ_ASSERT(IsAsciiAlphanumeric(c));
175 digit = AsciiAlphanumericToNumber(c);
176 digitMask = base >> 1;
179 int bit = (digit & digitMask) != 0;
180 digitMask >>= 1;
181 return bit;
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. */
202 int bit;
203 do {
204 bit = bdr.nextDigit();
205 } while (bit == 0);
207 MOZ_ASSERT(bit == 1); // guaranteed by Get{Prefix,Decimal}Integer
209 /* Gather the 53 significant bits (including the leading 1). */
210 double value = 1.0;
211 for (int j = 52; j > 0; j--) {
212 bit = bdr.nextDigit();
213 if (bit < 0) {
214 return value;
216 value = value * 2 + bit;
219 /* bit2 is the 54th bit (the first dropped from the mantissa). */
220 int bit2 = bdr.nextDigit();
221 if (bit2 >= 0) {
222 double factor = 2.0;
223 int sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */
224 int bit3;
226 while ((bit3 = bdr.nextDigit()) >= 0) {
227 sticky |= bit3;
228 factor *= 2;
230 value += bit2 & (bit | sticky);
231 value *= factor;
234 return value;
237 template <typename CharT>
238 double js::ParseDecimalNumber(const mozilla::Range<const CharT> chars) {
239 MOZ_ASSERT(chars.length() > 0);
240 uint64_t dec = 0;
241 RangedPtr<const CharT> s = chars.begin(), end = chars.end();
242 do {
243 CharT c = *s;
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");
249 dec = next;
250 } while (++s < end);
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;
268 double d = 0.0;
269 for (; s < end; s++) {
270 CharT c = *s;
271 if (!IsAsciiAlphanumeric(c)) {
272 if (c == '_' &&
273 separatorHandling == IntegerSeparatorHandling::SkipUnderscore) {
274 AssertWellPlacedNumericSeparator(s, start, end);
275 continue;
277 break;
280 uint8_t digit = AsciiAlphanumericToNumber(c);
281 if (digit >= base) {
282 break;
285 d = d * base + digit;
288 *endp = s;
289 *dp = d;
291 /* If we haven't reached the limit of integer precision, we're done. */
292 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
293 return true;
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.
301 if (base == 10) {
302 return false;
305 if ((base & (base - 1)) == 0) {
306 *dp = ComputeAccurateBinaryBaseInteger(start, s, base);
309 return true;
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)) {
317 return true;
320 // Can only fail for base 10.
321 MOZ_ASSERT(base == 10);
323 return ComputeAccurateDecimalInteger(cx, start, *endp, dp);
326 namespace js {
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);
338 } // namespace js
340 template <typename CharT>
341 bool js::GetDecimalInteger(JSContext* cx, const CharT* start, const CharT* end,
342 double* dp) {
343 MOZ_ASSERT(start <= end);
345 const CharT* s = start;
346 double d = 0.0;
347 for (; s < end; s++) {
348 CharT c = *s;
349 if (c == '_') {
350 AssertWellPlacedNumericSeparator(s, start, end);
351 continue;
353 MOZ_ASSERT(IsAsciiDigit(c));
354 int digit = c - '0';
355 d = d * 10 + digit;
358 *dp = d;
360 // If we haven't reached the limit of integer precision, we're done.
361 if (d < DOUBLE_INTEGRAL_PRECISION_LIMIT) {
362 return true;
365 // Otherwise compute the correct integer from the prefix of valid digits.
366 return ComputeAccurateDecimalInteger(cx, start, s, dp);
369 namespace js {
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);
377 template <>
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);
384 } // namespace js
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)) {
394 return false;
397 const CharT* s = start;
398 size_t i = 0;
399 for (; s < end; s++) {
400 CharT c = *s;
401 if (c == '_') {
402 AssertWellPlacedNumericSeparator(s, start, end);
403 continue;
405 MOZ_ASSERT(IsAsciiDigit(c) || c == '.' || c == 'e' || c == 'E' ||
406 c == '+' || c == '-');
407 chars[i++] = char(c);
409 chars[i] = 0;
411 if (!EnsureDtoaState(cx)) {
412 return false;
415 char* ep;
416 *dp = js_strtod_harder(cx->dtoaState, chars.begin(), &ep);
417 MOZ_ASSERT(ep >= chars.begin());
419 return true;
422 namespace js {
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);
430 template <>
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);
437 } // namespace js
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();
444 return true;
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);
451 } else {
452 args.rval().set(args[0]);
454 return true;
457 JSString* str = ToString<CanGC>(cx, args[0]);
458 if (!str) {
459 return false;
462 if (str->hasIndexValue()) {
463 args.rval().setNumber(str->getIndexValue());
464 return true;
467 JSLinearString* linear = str->ensureLinear(cx);
468 if (!linear) {
469 return false;
472 double d;
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)) {
478 return false;
480 if (end == begin) {
481 d = GenericNaN();
483 } else {
484 const char16_t* begin = linear->twoByteChars(nogc);
485 const char16_t* end;
486 if (!js_strtod(cx, begin, begin + linear->length(), &end, &d)) {
487 return false;
489 if (end == begin) {
490 d = GenericNaN();
494 args.rval().setDouble(d);
495 return true;
498 template <typename CharT>
499 static bool ParseIntImpl(JSContext* cx, const CharT* chars, size_t length,
500 bool stripPrefix, int32_t radix, double* res) {
501 /* Step 2. */
502 const CharT* end = chars + length;
503 const CharT* s = SkipSpace(chars, end);
505 MOZ_ASSERT(chars <= s);
506 MOZ_ASSERT(s <= end);
508 /* Steps 3-4. */
509 bool negative = (s != end && s[0] == '-');
511 /* Step 5. */
512 if (s != end && (s[0] == '-' || s[0] == '+')) {
513 s++;
516 /* Step 10. */
517 if (stripPrefix) {
518 if (end - s >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
519 s += 2;
520 radix = 16;
524 /* Steps 11-15. */
525 const CharT* actualEnd;
526 double d;
527 if (!GetPrefixInteger(cx, s, end, radix, IntegerSeparatorHandling::None,
528 &actualEnd, &d)) {
529 return false;
532 if (s == actualEnd) {
533 *res = GenericNaN();
534 } else {
535 *res = negative ? -d : d;
537 return true;
540 /* ES5 15.1.2.2. */
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();
547 return true;
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]);
554 return true;
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));
572 return true;
574 if (-1.0e21 < d && d < -1.0e-6) {
575 args.rval().setNumber(-floor(-d));
576 return true;
578 if (d == 0.0) {
579 args.rval().setInt32(0);
580 return true;
584 if (args[0].isString()) {
585 JSString* str = args[0].toString();
586 if (str->hasIndexValue()) {
587 args.rval().setNumber(str->getIndexValue());
588 return true;
593 /* Step 1. */
594 RootedString inputString(cx, ToString<CanGC>(cx, args[0]));
595 if (!inputString) {
596 return false;
598 args[0].setString(inputString);
600 /* Steps 6-9. */
601 bool stripPrefix = true;
602 int32_t radix;
603 if (!args.hasDefined(1)) {
604 radix = 10;
605 } else {
606 if (!ToInt32(cx, args[1], &radix)) {
607 return false;
609 if (radix == 0) {
610 radix = 10;
611 } else {
612 if (radix < 2 || radix > 36) {
613 args.rval().setNaN();
614 return true;
616 if (radix != 16) {
617 stripPrefix = false;
622 JSLinearString* linear = inputString->ensureLinear(cx);
623 if (!linear) {
624 return false;
627 AutoCheckCannotGC nogc;
628 size_t length = inputString->length();
629 double number;
630 if (linear->hasLatin1Chars()) {
631 if (!ParseIntImpl(cx, linear->latin1Chars(nogc), length, stripPrefix, radix,
632 &number)) {
633 return false;
635 } else {
636 if (!ParseIntImpl(cx, linear->twoByteChars(nogc), length, stripPrefix,
637 radix, &number)) {
638 return false;
642 args.rval().setNumber(number);
643 return true;
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),
649 JS_FS_END};
651 const JSClass NumberObject::class_ = {
652 js_Number_str,
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])) {
662 return false;
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]);
673 } else {
674 args.rval().setInt32(0);
676 return true;
679 RootedObject proto(cx);
680 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Number, &proto)) {
681 return false;
684 double d = args.length() > 0 ? args[0].toNumber() : 0;
685 JSObject* obj = NumberObject::create(cx, d, proto);
686 if (!obj) {
687 return false;
689 args.rval().setObject(*obj);
690 return true;
693 // ES2020 draft rev e08b018785606bc6465a0456a79604b149007932
694 // 20.1.3 Properties of the Number Prototype Object, thisNumberValue.
695 MOZ_ALWAYS_INLINE
696 static bool ThisNumberValue(JSContext* cx, const CallArgs& args,
697 const char* methodName, double* number) {
698 HandleValue thisv = args.thisv();
700 // Step 1.
701 if (thisv.isNumber()) {
702 *number = thisv.toNumber();
703 return true;
706 // Steps 2-3.
707 auto* obj = UnwrapAndTypeCheckThis<NumberObject>(cx, args, methodName);
708 if (!obj) {
709 return false;
712 *number = obj->unbox();
713 return true;
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,
719 Value* vp) {
720 CallArgs args = CallArgsFromVp(argc, vp);
722 double d;
723 if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
724 return false;
727 args.rval().setNumber(d);
728 return true;
731 static bool num_toSource(JSContext* cx, unsigned argc, Value* vp) {
732 CallArgs args = CallArgsFromVp(argc, vp);
734 double d;
735 if (!ThisNumberValue(cx, args, "toSource", &d)) {
736 return false;
739 JSStringBuilder sb(cx);
740 if (!sb.append("(new Number(") ||
741 !NumberValueToStringBuffer(cx, NumberValue(d), sb) || !sb.append("))")) {
742 return false;
745 JSString* str = sb.finishString();
746 if (!str) {
747 return false;
749 args.rval().setString(str);
750 return true;
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); }
761 MOZ_ALWAYS_INLINE
762 static JSLinearString* LookupDtoaCache(JSContext* cx, double d) {
763 if (Realm* realm = cx->realm()) {
764 if (JSLinearString* str = realm->dtoaCache.lookup(10, d)) {
765 return str;
769 return nullptr;
772 MOZ_ALWAYS_INLINE
773 static void CacheNumber(JSContext* cx, double d, JSLinearString* str) {
774 if (Realm* realm = cx->realm()) {
775 realm->dtoaCache.cache(10, d, str);
779 MOZ_ALWAYS_INLINE
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);
795 *end = '\0';
796 RangedPtr<T> start = BackfillIndexInCharBuffer(ui, end);
797 if (si < 0) {
798 *--start = '-';
801 *length = end - start;
802 return start.get();
805 template <AllowGC allowGC>
806 JSLinearString* js::Int32ToString(JSContext* cx, int32_t si) {
807 if (JSLinearString* str = LookupInt32ToString(cx, si)) {
808 return str;
811 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
812 size_t length;
813 Latin1Char* start =
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);
819 if (!str) {
820 return nullptr;
822 if (si >= 0) {
823 str->maybeInitializeIndexValue(si);
826 CacheNumber(cx, si, str);
827 return 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];
845 size_t length;
846 char* start = BackfillInt32InBuffer(
847 si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
849 Maybe<uint32_t> indexValue;
850 if (si >= 0) {
851 indexValue.emplace(si);
854 JSAtom* atom = Atomize(cx, start, length, indexValue);
855 if (!atom) {
856 return nullptr;
859 CacheNumber(cx, si, atom);
860 return atom;
863 frontend::TaggedParserAtomIndex js::Int32ToParserAtom(
864 JSContext* cx, frontend::ParserAtomsTable& parserAtoms, int32_t si) {
865 char buffer[JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1];
866 size_t length;
867 char* start = BackfillInt32InBuffer(
868 si, buffer, JSFatInlineString::MAX_LENGTH_TWO_BYTE + 1, &length);
870 Maybe<uint32_t> indexValue;
871 if (si >= 0) {
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,
880 int base = 10) {
881 uint32_t u = Abs(i);
883 RangedPtr<char> cp(cbuf->sbuf + ToCStringBuf::sbufSize - 1, cbuf->sbuf,
884 ToCStringBuf::sbufSize);
885 char* end = cp.get();
886 *cp = '\0';
888 /* Build the string from behind. */
889 switch (base) {
890 case 10:
891 cp = BackfillIndexInCharBuffer(u, cp);
892 break;
893 case 16:
894 do {
895 unsigned newu = u / 16;
896 *--cp = "0123456789abcdef"[u - newu * 16];
897 u = newu;
898 } while (u != 0);
899 break;
900 default:
901 MOZ_ASSERT(base >= 2 && base <= 36);
902 do {
903 unsigned newu = u / base;
904 *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
905 u = newu;
906 } while (u != 0);
907 break;
909 if (i < 0) {
910 *--cp = '-';
913 *len = end - cp.get();
914 return 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);
923 double d;
924 if (!ThisNumberValue(cx, args, "toString", &d)) {
925 return false;
928 int32_t base = 10;
929 if (args.hasDefined(0)) {
930 double d2;
931 if (!ToInteger(cx, args[0], &d2)) {
932 return false;
935 if (d2 < 2 || d2 > 36) {
936 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
937 return false;
940 base = int32_t(d2);
942 JSString* str = NumberToStringWithBase<CanGC>(cx, d, base);
943 if (!str) {
944 return false;
946 args.rval().setString(str);
947 return true;
950 #if !JS_HAS_INTL_API
951 static bool num_toLocaleString(JSContext* cx, unsigned argc, Value* vp) {
952 CallArgs args = CallArgsFromVp(argc, vp);
954 double d;
955 if (!ThisNumberValue(cx, args, "toLocaleString", &d)) {
956 return false;
959 RootedString str(cx, NumberToStringWithBase<CanGC>(cx, d, 10));
960 if (!str) {
961 return false;
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);
969 if (!numBytes) {
970 return false;
972 const char* num = numBytes.get();
973 if (!num) {
974 return false;
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;
982 if (*nint == '-') {
983 nint++;
985 while (*nint >= '0' && *nint <= '9') {
986 nint++;
988 int digits = nint - num;
989 const char* end = num + digits;
990 if (!digits) {
991 args.rval().setString(str);
992 return true;
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);
1001 if (*nint == '.') {
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;
1009 if (*num == '-') {
1010 remainder--;
1013 while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
1014 if (*tmpGroup >= remainder) {
1015 break;
1017 buflen += thousandsLength;
1018 remainder -= *tmpGroup;
1019 tmpGroup++;
1022 int nrepeat;
1023 if (*tmpGroup == '\0' && *numGrouping != '\0') {
1024 nrepeat = (remainder - 1) / tmpGroup[-1];
1025 buflen += thousandsLength * nrepeat;
1026 remainder -= nrepeat * tmpGroup[-1];
1027 } else {
1028 nrepeat = 0;
1030 tmpGroup--;
1032 char* buf = cx->pod_malloc<char>(buflen + 1);
1033 if (!buf) {
1034 return false;
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) {
1053 tmpGroup--;
1057 if (*nint == '.') {
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);
1063 } else {
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);
1072 if (ok) {
1073 args.rval().set(v);
1075 js_free(buf);
1076 return ok;
1079 str = NewStringCopyN<CanGC>(cx, buf, buflen);
1080 js_free(buf);
1081 if (!str) {
1082 return false;
1085 args.rval().setString(str);
1086 return true;
1088 #endif /* !JS_HAS_INTL_API */
1090 bool js::num_valueOf(JSContext* cx, unsigned argc, Value* vp) {
1091 CallArgs args = CallArgsFromVp(argc, vp);
1093 double d;
1094 if (!ThisNumberValue(cx, args, "valueOf", &d)) {
1095 return false;
1098 args.rval().setNumber(d);
1099 return true;
1102 static const unsigned MAX_PRECISION = 100;
1104 static bool ComputePrecisionInRange(JSContext* cx, int minPrecision,
1105 int maxPrecision, double prec,
1106 int* precision) {
1107 if (minPrecision <= prec && prec <= maxPrecision) {
1108 *precision = int(prec);
1109 return true;
1112 ToCStringBuf cbuf;
1113 if (char* numStr = NumberToCString(cx, &cbuf, prec, 10)) {
1114 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1115 JSMSG_PRECISION_RANGE, numStr);
1117 return false;
1120 static constexpr size_t DoubleToStrResultBufSize = 128;
1122 template <typename Op>
1123 [[nodiscard]] static bool DoubleToStrResult(JSContext* cx, const CallArgs& args,
1124 Op op) {
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);
1138 if (!str) {
1139 return false;
1142 args.rval().setString(str);
1143 return true;
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);
1150 // Step 1.
1151 double d;
1152 if (!ThisNumberValue(cx, args, "toFixed", &d)) {
1153 return false;
1156 // Steps 2-5.
1157 int precision;
1158 if (args.length() == 0) {
1159 precision = 0;
1160 } else {
1161 double prec = 0;
1162 if (!ToInteger(cx, args[0], &prec)) {
1163 return false;
1166 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
1167 return false;
1171 // Step 6.
1172 if (mozilla::IsNaN(d)) {
1173 args.rval().setString(cx->names().NaN);
1174 return true;
1176 if (mozilla::IsInfinite(d)) {
1177 if (d > 0) {
1178 args.rval().setString(cx->names().Infinity);
1179 return true;
1182 args.rval().setString(cx->names().NegativeInfinity);
1183 return true;
1186 // Steps 7-10 for very large numbers.
1187 if (d <= -1e21 || d >= 1e+21) {
1188 JSString* s = NumberToString<CanGC>(cx, d);
1189 if (!s) {
1190 return false;
1193 args.rval().setString(s);
1194 return true;
1197 // Steps 7-12.
1199 // DoubleToStringConverter::ToFixed is documented as requiring a buffer size
1200 // of:
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);
1224 // Step 1.
1225 double d;
1226 if (!ThisNumberValue(cx, args, "toExponential", &d)) {
1227 return false;
1230 // Step 2.
1231 double prec = 0;
1232 if (args.hasDefined(0)) {
1233 if (!ToInteger(cx, args[0], &prec)) {
1234 return false;
1238 // Step 3.
1239 MOZ_ASSERT_IF(!args.hasDefined(0), prec == 0);
1241 // Step 4.
1242 if (mozilla::IsNaN(d)) {
1243 args.rval().setString(cx->names().NaN);
1244 return true;
1246 if (mozilla::IsInfinite(d)) {
1247 if (d > 0) {
1248 args.rval().setString(cx->names().Infinity);
1249 return true;
1252 args.rval().setString(cx->names().NegativeInfinity);
1253 return true;
1256 // Step 5.
1257 int precision = 0;
1258 if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, prec, &precision)) {
1259 return false;
1262 // Steps 6-15.
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);
1281 // Step 1.
1282 double d;
1283 if (!ThisNumberValue(cx, args, "toPrecision", &d)) {
1284 return false;
1287 // Step 2.
1288 if (!args.hasDefined(0)) {
1289 JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10);
1290 if (!str) {
1291 return false;
1293 args.rval().setString(str);
1294 return true;
1297 // Step 3.
1298 double prec = 0;
1299 if (!ToInteger(cx, args[0], &prec)) {
1300 return false;
1303 // Step 4.
1304 if (mozilla::IsNaN(d)) {
1305 args.rval().setString(cx->names().NaN);
1306 return true;
1308 if (mozilla::IsInfinite(d)) {
1309 if (d > 0) {
1310 args.rval().setString(cx->names().Infinity);
1311 return true;
1314 args.rval().setString(cx->names().NegativeInfinity);
1315 return true;
1318 // Step 5.
1319 int precision = 0;
1320 if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, prec, &precision)) {
1321 return false;
1324 // Steps 6-14.
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'
1330 // character.
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),
1341 #if JS_HAS_INTL_API
1342 JS_SELF_HOSTED_FN(js_toLocaleString_str, "Number_toLocaleString", 0, 0),
1343 #else
1344 JS_FN(js_toLocaleString_str, num_toLocaleString, 0, 0),
1345 #endif
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),
1350 JS_FS_END};
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),
1365 JS_FS_END};
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),
1385 JS_PS_END};
1387 bool js::InitRuntimeNumberState(JSRuntime* rt) {
1388 // XXX If JS_HAS_INTL_API becomes true all the time at some point,
1389 // js::InitRuntimeNumberState is no longer fallible, and we should
1390 // change its return type.
1391 #if !JS_HAS_INTL_API
1392 /* Copy locale-specific separators into the runtime strings. */
1393 const char* thousandsSeparator;
1394 const char* decimalPoint;
1395 const char* grouping;
1396 # ifdef HAVE_LOCALECONV
1397 struct lconv* locale = localeconv();
1398 thousandsSeparator = locale->thousands_sep;
1399 decimalPoint = locale->decimal_point;
1400 grouping = locale->grouping;
1401 # else
1402 thousandsSeparator = getenv("LOCALE_THOUSANDS_SEP");
1403 decimalPoint = getenv("LOCALE_DECIMAL_POINT");
1404 grouping = getenv("LOCALE_GROUPING");
1405 # endif
1406 if (!thousandsSeparator) {
1407 thousandsSeparator = "'";
1409 if (!decimalPoint) {
1410 decimalPoint = ".";
1412 if (!grouping) {
1413 grouping = "\3\0";
1417 * We use single malloc to get the memory for all separator and grouping
1418 * strings.
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);
1426 if (!storage) {
1427 return false;
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 */
1441 return true;
1444 void js::FinishRuntimeNumberState(JSRuntime* rt) {
1445 #if !JS_HAS_INTL_API
1447 * The free also releases the memory for decimalSeparator and numGrouping
1448 * strings.
1450 char* storage = const_cast<char*>(rt->thousandsSeparator.ref());
1451 js_free(storage);
1452 #endif // !JS_HAS_INTL_API
1455 JSObject* NumberObject::createPrototype(JSContext* cx, JSProtoKey key) {
1456 NumberObject* numberProto =
1457 GlobalObject::createBlankPrototype<NumberObject>(cx, cx->global());
1458 if (!numberProto) {
1459 return nullptr;
1461 numberProto->setPrimitiveValue(0);
1462 return numberProto;
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)) {
1470 return false;
1473 // Number.parseInt should be the same function object as global parseInt.
1474 RootedId parseIntId(cx, NameToId(cx->names().parseInt));
1475 JSFunction* parseInt =
1476 DefineFunction(cx, global, parseIntId, num_parseInt, 2, JSPROP_RESOLVING);
1477 if (!parseInt) {
1478 return false;
1480 RootedValue parseIntValue(cx, ObjectValue(*parseInt));
1481 if (!DefineDataProperty(cx, ctor, parseIntId, parseIntValue, 0)) {
1482 return false;
1485 // Number.parseFloat should be the same function object as global
1486 // parseFloat.
1487 RootedId parseFloatId(cx, NameToId(cx->names().parseFloat));
1488 JSFunction* parseFloat = DefineFunction(cx, global, parseFloatId,
1489 num_parseFloat, 1, JSPROP_RESOLVING);
1490 if (!parseFloat) {
1491 return false;
1493 RootedValue parseFloatValue(cx, ObjectValue(*parseFloat));
1494 if (!DefineDataProperty(cx, ctor, parseFloatId, parseFloatValue, 0)) {
1495 return false;
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)) {
1504 return false;
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)) {
1514 return false;
1517 return true;
1520 const ClassSpec NumberObject::classSpec_ = {
1521 GenericCreateConstructor<Number, 1, gc::AllocKind::FUNCTION>,
1522 NumberObject::createPrototype,
1523 number_static_methods,
1524 number_static_properties,
1525 number_methods,
1526 nullptr,
1527 NumberClassFinish};
1529 static char* FracNumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d,
1530 int base = 10) {
1531 #ifdef DEBUG
1533 int32_t _;
1534 MOZ_ASSERT(!NumberEqualsInt32(d, &_));
1536 #endif
1538 char* numStr;
1539 if (base == 10) {
1541 * This is V8's implementation of the algorithm described in the
1542 * following paper:
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();
1553 } else {
1554 if (!EnsureDtoaState(cx)) {
1555 return nullptr;
1557 numStr = cbuf->dbuf = js_dtobasestr(cx->dtoaState, base, d);
1559 return numStr;
1562 void JS::NumberToString(double d, char (&out)[MaximumNumberToStringLength]) {
1563 int32_t i;
1564 if (NumberEqualsInt32(d, &i)) {
1565 ToCStringBuf cbuf;
1566 size_t len;
1567 char* loc = Int32ToCString(&cbuf, i, &len, 10);
1568 memmove(out, loc, len);
1569 out[len] = '\0';
1570 } else {
1571 const double_conversion::DoubleToStringConverter& converter =
1572 double_conversion::DoubleToStringConverter::EcmaScriptConverter();
1574 double_conversion::StringBuilder builder(out, sizeof(out));
1575 converter.ToShortest(d, &builder);
1577 #ifdef DEBUG
1578 char* result =
1579 #endif
1580 builder.Finalize();
1581 MOZ_ASSERT(out == result);
1585 char* js::NumberToCString(JSContext* cx, ToCStringBuf* cbuf, double d,
1586 int base /* = 10*/) {
1587 int32_t i;
1588 size_t len;
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);
1597 ToCStringBuf cbuf;
1598 char* numStr;
1599 size_t numStrLen;
1601 Realm* realm = cx->realm();
1603 int32_t i;
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)) {
1611 if (i < 10) {
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);
1622 MOZ_ASSERT(str);
1623 return str;
1626 if (JSLinearString* str = realm->dtoaCache.lookup(base, d)) {
1627 return str;
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));
1634 } else {
1635 if (JSLinearString* str = realm->dtoaCache.lookup(base, d)) {
1636 return str;
1639 numStr = FracNumberToCString(cx, &cbuf, d, base);
1640 if (!numStr) {
1641 if constexpr (allowGC) {
1642 ReportOutOfMemory(cx);
1644 return nullptr;
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);
1653 JSLinearString* s =
1654 NewStringCopyN<allowGC>(cx, numStr, numStrLen, js::gc::DefaultHeap);
1655 if (!s) {
1656 return nullptr;
1659 if (isBase10Int && i >= 0) {
1660 s->maybeInitializeIndexValue(i);
1663 realm->dtoaCache.cache(base, d, s);
1664 return 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) {
1682 int32_t si;
1683 if (NumberEqualsInt32(d, &si)) {
1684 return Int32ToAtom(cx, si);
1687 if (JSLinearString* str = LookupDtoaCache(cx, d)) {
1688 return AtomizeString(cx, str);
1691 ToCStringBuf cbuf;
1692 char* numStr = FracNumberToCString(cx, &cbuf, d);
1693 if (!numStr) {
1694 ReportOutOfMemory(cx);
1695 return nullptr;
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);
1702 if (!atom) {
1703 return nullptr;
1706 CacheNumber(cx, d, atom);
1708 return atom;
1711 frontend::TaggedParserAtomIndex js::NumberToParserAtom(
1712 JSContext* cx, frontend::ParserAtomsTable& parserAtoms, double d) {
1713 int32_t si;
1714 if (NumberEqualsInt32(d, &si)) {
1715 return Int32ToParserAtom(cx, parserAtoms, si);
1718 ToCStringBuf cbuf;
1719 char* numStr = FracNumberToCString(cx, &cbuf, d);
1720 if (!numStr) {
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)) {
1738 return str;
1741 Latin1Char buffer[JSFatInlineString::MAX_LENGTH_LATIN1 + 1];
1742 RangedPtr<Latin1Char> end(buffer + JSFatInlineString::MAX_LENGTH_LATIN1,
1743 buffer, JSFatInlineString::MAX_LENGTH_LATIN1 + 1);
1744 *end = '\0';
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);
1749 if (!str) {
1750 return nullptr;
1753 realm->dtoaCache.cache(10, index, str);
1754 return str;
1757 bool js::NumberValueToStringBuffer(JSContext* cx, const Value& v,
1758 StringBuffer& sb) {
1759 /* Convert to C-string. */
1760 ToCStringBuf cbuf;
1761 const char* cstr;
1762 size_t cstrlen;
1763 if (v.isInt32()) {
1764 cstr = Int32ToCString(&cbuf, v.toInt32(), &cstrlen);
1765 MOZ_ASSERT(cstrlen == strlen(cstr));
1766 } else {
1767 cstr = NumberToCString(cx, &cbuf, v.toDouble());
1768 if (!cstr) {
1769 JS_ReportOutOfMemory(cx);
1770 return false;
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') {
1782 *result = c - '0';
1783 } else if (unicode::IsSpace(c)) {
1784 *result = 0.0;
1785 } else {
1786 *result = GenericNaN();
1790 template <typename CharT>
1791 inline bool CharsToNonDecimalNumber(const CharT* start, const CharT* end,
1792 double* result) {
1793 MOZ_ASSERT(end - start >= 2);
1794 MOZ_ASSERT(start[0] == '0');
1796 int radix = 0;
1797 if (start[1] == 'b' || start[1] == 'B') {
1798 radix = 2;
1799 } else if (start[1] == 'o' || start[1] == 'O') {
1800 radix = 8;
1801 } else if (start[1] == 'x' || start[1] == 'X') {
1802 radix = 16;
1803 } else {
1804 return false;
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
1809 // digits.
1810 const CharT* endptr;
1811 double d;
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();
1816 } else {
1817 *result = d;
1819 return true;
1822 template <typename CharT>
1823 bool js::CharsToNumber(JSContext* cx, const CharT* chars, size_t length,
1824 double* result) {
1825 if (length == 1) {
1826 CharToNumber(chars[0], result);
1827 return true;
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)) {
1836 return true;
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.
1847 const CharT* ep;
1848 double d;
1849 if (!js_strtod(cx, start, end, &ep, &d)) {
1850 *result = GenericNaN();
1851 return false;
1854 if (SkipSpace(ep, end) != end) {
1855 *result = GenericNaN();
1856 } else {
1857 *result = d;
1860 return true;
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) {
1871 if (length == 1) {
1872 CharToNumber(chars[0], result);
1873 return true;
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)) {
1882 return true;
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;
1892 double d;
1893 if (!GetPrefixInteger(start, end, 10, IntegerSeparatorHandling::None, &endptr,
1894 &d) ||
1895 SkipSpace(endptr, end) != end) {
1896 return false;
1899 *result = d;
1900 return true;
1903 bool js::StringToNumber(JSContext* cx, JSString* str, double* result) {
1904 AutoCheckCannotGC nogc;
1905 JSLinearString* linearStr = str->ensureLinear(cx);
1906 if (!linearStr) {
1907 return false;
1910 if (str->hasIndexValue()) {
1911 *result = str->getIndexValue();
1912 return true;
1915 return linearStr->hasLatin1Chars()
1916 ? CharsToNumber(cx, linearStr->latin1Chars(nogc), str->length(),
1917 result)
1918 : CharsToNumber(cx, linearStr->twoByteChars(nogc), str->length(),
1919 result);
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();
1928 return false;
1930 return true;
1933 bool js::MaybeStringToNumber(JSLinearString* str, double* result) {
1934 AutoCheckCannotGC nogc;
1936 if (str->hasIndexValue()) {
1937 *result = str->getIndexValue();
1938 return true;
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_,
1947 double* out) {
1948 RootedValue v(cx, v_);
1949 MOZ_ASSERT(!v.isNumber());
1951 if (!v.isPrimitive()) {
1952 if (cx->isHelperThreadContext()) {
1953 return false;
1956 if (!ToPrimitive(cx, JSTYPE_NUMBER, &v)) {
1957 return false;
1960 if (v.isNumber()) {
1961 *out = v.toNumber();
1962 return true;
1965 if (v.isString()) {
1966 return StringToNumber(cx, v.toString(), out);
1968 if (v.isBoolean()) {
1969 *out = v.toBoolean() ? 1.0 : 0.0;
1970 return true;
1972 if (v.isNull()) {
1973 *out = 0.0;
1974 return true;
1976 if (v.isUndefined()) {
1977 *out = GenericNaN();
1978 return true;
1980 #ifdef ENABLE_RECORD_TUPLE
1981 if (v.isExtendedPrimitive()) {
1982 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1983 JSMSG_RECORD_TUPLE_TO_NUMBER);
1984 return false;
1986 #endif
1988 MOZ_ASSERT(v.isSymbol() || v.isBigInt());
1989 if (!cx->isHelperThreadContext()) {
1990 unsigned errnum = JSMSG_SYMBOL_TO_NUMBER;
1991 if (v.isBigInt()) {
1992 errnum = JSMSG_BIGINT_TO_NUMBER;
1994 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errnum);
1996 return false;
1999 // BigInt proposal section 3.1.6
2000 bool js::ToNumericSlow(JSContext* cx, MutableHandleValue vp) {
2001 MOZ_ASSERT(!vp.isNumeric());
2003 // Step 1.
2004 if (!vp.isPrimitive()) {
2005 if (cx->isHelperThreadContext()) {
2006 return false;
2008 if (!ToPrimitive(cx, JSTYPE_NUMBER, vp)) {
2009 return false;
2013 // Step 2.
2014 if (vp.isBigInt()) {
2015 return true;
2018 // Step 3.
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,
2027 int8_t* out) {
2028 MOZ_ASSERT(!v.isInt32());
2029 double d;
2030 if (v.isDouble()) {
2031 d = v.toDouble();
2032 } else {
2033 if (!ToNumberSlow(cx, v, &d)) {
2034 return false;
2037 *out = ToInt8(d);
2038 return true;
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
2044 * failure.
2046 JS_PUBLIC_API bool js::ToUint8Slow(JSContext* cx, const HandleValue v,
2047 uint8_t* out) {
2048 MOZ_ASSERT(!v.isInt32());
2049 double d;
2050 if (v.isDouble()) {
2051 d = v.toDouble();
2052 } else {
2053 if (!ToNumberSlow(cx, v, &d)) {
2054 return false;
2057 *out = ToUint8(d);
2058 return true;
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,
2066 int16_t* out) {
2067 MOZ_ASSERT(!v.isInt32());
2068 double d;
2069 if (v.isDouble()) {
2070 d = v.toDouble();
2071 } else {
2072 if (!ToNumberSlow(cx, v, &d)) {
2073 return false;
2076 *out = ToInt16(d);
2077 return true;
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,
2085 int64_t* out) {
2086 MOZ_ASSERT(!v.isInt32());
2087 double d;
2088 if (v.isDouble()) {
2089 d = v.toDouble();
2090 } else {
2091 if (!ToNumberSlow(cx, v, &d)) {
2092 return false;
2095 *out = ToInt64(d);
2096 return true;
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
2102 * failure.
2104 JS_PUBLIC_API bool js::ToUint64Slow(JSContext* cx, const HandleValue v,
2105 uint64_t* out) {
2106 MOZ_ASSERT(!v.isInt32());
2107 double d;
2108 if (v.isDouble()) {
2109 d = v.toDouble();
2110 } else {
2111 if (!ToNumberSlow(cx, v, &d)) {
2112 return false;
2115 *out = ToUint64(d);
2116 return true;
2119 JS_PUBLIC_API bool js::ToInt32Slow(JSContext* cx, const HandleValue v,
2120 int32_t* out) {
2121 MOZ_ASSERT(!v.isInt32());
2122 double d;
2123 if (v.isDouble()) {
2124 d = v.toDouble();
2125 } else {
2126 if (!ToNumberSlow(cx, v, &d)) {
2127 return false;
2130 *out = ToInt32(d);
2131 return true;
2134 bool js::ToInt32OrBigIntSlow(JSContext* cx, MutableHandleValue vp) {
2135 MOZ_ASSERT(!vp.isInt32());
2136 if (vp.isDouble()) {
2137 vp.setInt32(ToInt32(vp.toDouble()));
2138 return true;
2141 if (!ToNumeric(cx, vp)) {
2142 return false;
2145 if (vp.isBigInt()) {
2146 return true;
2149 vp.setInt32(ToInt32(vp.toNumber()));
2150 return true;
2153 JS_PUBLIC_API bool js::ToUint32Slow(JSContext* cx, const HandleValue v,
2154 uint32_t* out) {
2155 MOZ_ASSERT(!v.isInt32());
2156 double d;
2157 if (v.isDouble()) {
2158 d = v.toDouble();
2159 } else {
2160 if (!ToNumberSlow(cx, v, &d)) {
2161 return false;
2164 *out = ToUint32(d);
2165 return true;
2168 JS_PUBLIC_API bool js::ToUint16Slow(JSContext* cx, const HandleValue v,
2169 uint16_t* out) {
2170 MOZ_ASSERT(!v.isInt32());
2171 double d;
2172 if (v.isDouble()) {
2173 d = v.toDouble();
2174 } else if (!ToNumberSlow(cx, v, &d)) {
2175 return false;
2177 *out = ToUint16(d);
2178 return true;
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);
2186 // Step 1.
2187 if (v.isUndefined()) {
2188 *index = 0;
2189 return true;
2192 // Step 2.a.
2193 double integerIndex;
2194 if (!ToInteger(cx, v, &integerIndex)) {
2195 return false;
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);
2204 return false;
2207 // Step 3.
2208 *index = uint64_t(integerIndex);
2209 return true;
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)) {
2220 return false;
2223 size_t i = 0;
2224 for (; i < length; i++) {
2225 char16_t c = s[i];
2226 if (c >> 8) {
2227 break;
2229 chars[i] = char(c);
2231 chars[i] = 0;
2233 /* Try to parse +Infinity, -Infinity or Infinity. */
2235 char* afterSign = chars.begin();
2236 bool negative = (*afterSign == '-');
2237 if (negative || *afterSign == '+') {
2238 afterSign++;
2241 if (*afterSign == 'I' && !strncmp(afterSign, "Infinity", 8)) {
2242 *d = negative ? NegativeInfinity<double>() : PositiveInfinity<double>();
2243 *dEnd = s + (afterSign - chars.begin()) + 8;
2244 return true;
2248 if (!EnsureDtoaState(cx)) {
2249 return false;
2252 /* Everything else. */
2253 char* ep;
2254 *d = js_strtod_harder(cx->dtoaState, chars.begin(), &ep);
2256 MOZ_ASSERT(ep >= chars.begin());
2258 if (ep == chars.begin()) {
2259 *dEnd = begin;
2260 } else {
2261 *dEnd = s + (ep - chars.begin());
2264 return true;
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,
2272 double* d);