diff --git a/js/src/builtin/intl/NumberFormat.cpp b/js/src/builtin/intl/NumberFormat.cpp index 453ccc05e9..4dbc711d68 100644 --- a/js/src/builtin/intl/NumberFormat.cpp +++ b/js/src/builtin/intl/NumberFormat.cpp @@ -417,18 +417,26 @@ PartitionNumberPattern(JSContext* cx, UNumberFormat* nf, HandleValue x, }); } else if(x.isBigInt()) { RootedBigInt bi(cx, x.toBigInt()); - JSLinearString* str = BigInt::toString(cx, bi, 10); - if (!str) { - return nullptr; - } - MOZ_ASSERT(str->hasLatin1Chars()); + int64_t num; - JS::AutoCheckCannotGC noGC(cx); - const char* latinchars = reinterpret_cast(str->latin1Chars(noGC)); - size_t length = str->length(); - return CallICU(cx, [nf, latinchars, length](UChar* chars, int32_t size, UErrorCode* status) { - return unum_formatDecimal(nf, latinchars, length, chars, size, nullptr, status); - }); + if (BigInt::isInt64(bi, &num)) { + return CallICU(cx, [nf, num](UChar* chars, int32_t size, UErrorCode* status) { + return unum_formatInt64(nf, num, chars, size, nullptr, status); + }); + } else { + JSLinearString* str = BigInt::toString(cx, bi, 10); + if (!str) { + return nullptr; + } + MOZ_ASSERT(str->hasLatin1Chars()); + + JS::AutoCheckCannotGC noGC(cx); + const char* latinchars = reinterpret_cast(str->latin1Chars(noGC)); + size_t length = str->length(); + return CallICU(cx, [nf, latinchars, length](UChar* chars, int32_t size, UErrorCode* status) { + return unum_formatDecimal(nf, latinchars, length, chars, size, nullptr, status); + }); + } } return nullptr; } diff --git a/js/src/vm/BigIntType.cpp b/js/src/vm/BigIntType.cpp index 44bb1923f7..8382c641fa 100644 --- a/js/src/vm/BigIntType.cpp +++ b/js/src/vm/BigIntType.cpp @@ -87,6 +87,7 @@ #include "mozilla/WrappingOperations.h" #include +#include #include #include @@ -2211,6 +2212,43 @@ uint64_t BigInt::toUint64(BigInt* x) { return digit; } +bool BigInt::isInt64(BigInt* x, int64_t* result) { + MOZ_MAKE_MEM_UNDEFINED(result, sizeof(*result)); + + size_t length = x->digitLength(); + if (length > (DigitBits == 32 ? 2 : 1)) { + return false; + } + + if (length == 0) { + *result = 0; + return true; + } + + uint64_t magnitude = x->digit(0); + if (DigitBits == 32 && length > 1) { + magnitude |= static_cast(x->digit(1)) << 32; + } + + if (x->isNegative()) { + constexpr uint64_t Int64MinMagnitude = uint64_t(1) << 63; + if (magnitude <= Int64MinMagnitude) { + *result = magnitude == Int64MinMagnitude + ? std::numeric_limits::min() + : -AssertedCast(magnitude); + return true; + } + } else { + if (magnitude <= + static_cast(std::numeric_limits::max())) { + *result = AssertedCast(magnitude); + return true; + } + } + + return false; +} + // Compute `2**bits - (x & (2**bits - 1))`. Used when treating BigInt values as // arbitrary-precision two's complement signed integers. BigInt* BigInt::truncateAndSubFromPowerOfTwo(ExclusiveContext* cx, HandleBigInt x, @@ -2661,7 +2699,7 @@ double BigInt::numberValue(BigInt* x) { if (length <= 64 / DigitBits) { uint64_t magnitude = x->digit(0); if (DigitBits == 32 && length > 1) { - magnitude |= uint64_t(x->digit(1)) << 32; + magnitude |= static_cast(x->digit(1)) << 32; } const uint64_t MaxIntegralPrecisionDouble = uint64_t(1) << (SignificandWidth + 1); diff --git a/js/src/vm/BigIntType.h b/js/src/vm/BigIntType.h index 7cdd757994..ea0317fd9c 100644 --- a/js/src/vm/BigIntType.h +++ b/js/src/vm/BigIntType.h @@ -119,6 +119,11 @@ class BigInt final : public js::gc::TenuredCell { static int64_t toInt64(BigInt* x); static uint64_t toUint64(BigInt* x); + // Return true if the BigInt is without loss of precision representable as an + // int64 and store the int64 value in the output. Otherwise return false and + // leave the value of the output parameter unspecified. + static bool isInt64(BigInt* x, int64_t* result); + static BigInt* asIntN(js::ExclusiveContext* cx, Handle x, uint64_t bits); static BigInt* asUintN(js::ExclusiveContext* cx, Handle x, uint64_t bits);