/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/FloatingPoint.h" // mozilla::NumberIsInt32 #include "mozilla/Range.h" // mozilla::Range #include "mozilla/Span.h" // mozilla::MakeStringSpan #include #include "js/BigInt.h" // JS::{,Number,String,SimpleString}ToBigInt, JS::ToBig{I,Ui}nt64 #include "js/CharacterEncoding.h" // JS::Const{Latin1,TwoByte}Chars #include "js/Conversions.h" // JS::ToString #include "js/ErrorReport.h" // JS::ErrorReportBuilder, JSEXN_SYNTAXERR #include "js/Exception.h" // JS::StealPendingExceptionStack, JS_IsExceptionPending #include "js/friend/ErrorMessages.h" // JSMSG_* #include "js/RootingAPI.h" // JS::Rooted #include "js/String.h" // JS_StringEqualsLiteral #include "js/Value.h" // JS::FalseValue, JS::Value #include "jsapi-tests/tests.h" #include "util/Text.h" // js::InflateString struct JS_PUBLIC_API JSContext; class JS_PUBLIC_API JSString; namespace JS { class JS_PUBLIC_API BigInt; } // namespace JS BEGIN_TEST(testToBigInt64) { JS::Rooted v(cx); EVAL("0n", &v); CHECK(v.isBigInt()); CHECK(JS::ToBigInt64(v.toBigInt()) == 0); EVAL("9223372036854775807n", &v); CHECK(v.isBigInt()); CHECK(JS::ToBigInt64(v.toBigInt()) == 9223372036854775807L); EVAL("-9223372036854775808n", &v); CHECK(v.isBigInt()); CHECK(JS::ToBigInt64(v.toBigInt()) == -9223372036854775807L - 1L); return true; } END_TEST(testToBigInt64) BEGIN_TEST(testToBigUint64) { JS::Rooted v(cx); EVAL("0n", &v); CHECK(v.isBigInt()); CHECK(JS::ToBigUint64(v.toBigInt()) == 0); EVAL("18446744073709551615n", &v); CHECK(v.isBigInt()); CHECK(JS::ToBigUint64(v.toBigInt()) == 18446744073709551615UL); return true; } END_TEST(testToBigUint64) #define GENERATE_INTTYPE_TEST(bits) \ BEGIN_TEST(testNumberToBigInt_Int##bits) { \ int##bits##_t i = INT##bits##_MIN; \ JS::BigInt* bi = JS::NumberToBigInt(cx, i); \ CHECK(bi); \ CHECK(JS::ToBigInt64(bi) == i); \ \ i = INT##bits##_MAX; \ bi = JS::NumberToBigInt(cx, i); \ CHECK(bi); \ CHECK(JS::ToBigInt64(bi) == i); \ \ uint##bits##_t u = 0; \ bi = JS::NumberToBigInt(cx, u); \ CHECK(bi); \ CHECK(JS::ToBigUint64(bi) == 0); \ \ u = UINT##bits##_MAX; \ bi = JS::NumberToBigInt(cx, u); \ CHECK(bi); \ CHECK(JS::ToBigUint64(bi) == u); \ \ return true; \ } \ END_TEST(testNumberToBigInt_Int##bits) GENERATE_INTTYPE_TEST(8); GENERATE_INTTYPE_TEST(16); GENERATE_INTTYPE_TEST(32); GENERATE_INTTYPE_TEST(64); #undef GENERATE_INTTYPE_TEST #define GENERATE_SIGNED_VALUE_TEST(type, tag, val) \ BEGIN_TEST(testNumberToBigInt_##type##_##tag) { \ type v = val; \ JS::BigInt* bi = JS::NumberToBigInt(cx, v); \ CHECK(bi); \ CHECK(JS::ToBigInt64(bi) == (val)); \ return true; \ } \ END_TEST(testNumberToBigInt_##type##_##tag) #define GENERATE_UNSIGNED_VALUE_TEST(type, tag, val) \ BEGIN_TEST(testNumberToBigInt_##type##_##tag) { \ type v = val; \ JS::BigInt* bi = JS::NumberToBigInt(cx, v); \ CHECK(bi); \ CHECK(JS::ToBigUint64(bi) == (val)); \ return true; \ } \ END_TEST(testNumberToBigInt_##type##_##tag) GENERATE_SIGNED_VALUE_TEST(int, zero, 0); GENERATE_SIGNED_VALUE_TEST(int, aValue, -42); GENERATE_UNSIGNED_VALUE_TEST(unsigned, zero, 0); GENERATE_UNSIGNED_VALUE_TEST(unsigned, aValue, 42); GENERATE_SIGNED_VALUE_TEST(long, zero, 0); GENERATE_SIGNED_VALUE_TEST(long, aValue, -42); GENERATE_UNSIGNED_VALUE_TEST(uintptr_t, zero, 0); GENERATE_UNSIGNED_VALUE_TEST(uintptr_t, aValue, 42); GENERATE_UNSIGNED_VALUE_TEST(size_t, zero, 0); GENERATE_UNSIGNED_VALUE_TEST(size_t, aValue, 42); GENERATE_SIGNED_VALUE_TEST(double, zero, 0); GENERATE_SIGNED_VALUE_TEST(double, aValue, -42); #undef GENERATE_SIGNED_VALUE_TEST #undef GENERATE_UNSIGNED_VALUE_TEST BEGIN_TEST(testNumberToBigInt_bool) { JS::BigInt* bi = JS::NumberToBigInt(cx, true); CHECK(bi); CHECK(JS::ToBigUint64(bi) == 1); bi = JS::NumberToBigInt(cx, false); CHECK(bi); CHECK(JS::ToBigUint64(bi) == 0); return true; } END_TEST(testNumberToBigInt_bool) BEGIN_TEST(testNumberToBigInt_NonIntegerValueFails) { JS::BigInt* bi = JS::NumberToBigInt(cx, 3.1416); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS_ClearPendingException(cx); return true; } END_TEST(testNumberToBigInt_NonIntegerValueFails) BEGIN_TEST(testStringToBigInt_FromTwoByteStringSpan) { mozilla::Range input{ mozilla::MakeStringSpan(u"18446744073709551616")}; JS::BigInt* bi = JS::StringToBigInt(cx, input); CHECK(bi); JS::Rooted val(cx, JS::BigIntValue(bi)); JS::Rooted str(cx, JS::ToString(cx, val)); CHECK(str); bool match; CHECK(JS_StringEqualsLiteral(cx, str, "18446744073709551616", &match)); CHECK(match); return true; } END_TEST(testStringToBigInt_FromTwoByteStringSpan) BEGIN_TEST(testStringToBigInt_FromLatin1Range) { const JS::Latin1Char string[] = "12345 and some junk at the end"; JS::ConstLatin1Chars range(string, 5); JS::BigInt* bi = JS::StringToBigInt(cx, range); CHECK(bi); CHECK(JS::ToBigInt64(bi) == 12345); return true; } END_TEST(testStringToBigInt_FromLatin1Range) BEGIN_TEST(testStringToBigInt_FromTwoByteRange) { const char16_t string[] = u"12345 and some junk at the end"; JS::ConstTwoByteChars range(string, 5); JS::BigInt* bi = JS::StringToBigInt(cx, range); CHECK(bi); CHECK(JS::ToBigInt64(bi) == 12345); return true; } END_TEST(testStringToBigInt_FromTwoByteRange) BEGIN_TEST(testStringToBigInt_AcceptedInput) { CHECK(Allowed(u"", 0)); CHECK(Allowed(u"\n", 0)); CHECK(Allowed(u" ", 0)); CHECK(Allowed(u"0\n", 0)); CHECK(Allowed(u"0 ", 0)); CHECK(Allowed(u"\n1", 1)); CHECK(Allowed(u" 1", 1)); CHECK(Allowed(u"\n2 ", 2)); CHECK(Allowed(u" 2\n", 2)); CHECK(Allowed(u"0b11", 3)); CHECK(Allowed(u"0x17", 23)); CHECK(Allowed(u"-5", -5)); CHECK(Allowed(u"+5", 5)); CHECK(Allowed(u"-0", 0)); CHECK(Fails(u"!!!!!!111one1111one1!1!1!!")); CHECK(Fails(u"3.1416")); CHECK(Fails(u"6.022e23")); CHECK(Fails(u"1e3")); CHECK(Fails(u".25")); CHECK(Fails(u".25e2")); CHECK(Fails(u"1_000_000")); CHECK(Fails(u"3n")); CHECK(Fails(u"-0x3")); CHECK(Fails(u"Infinity")); return true; } template inline bool Allowed(const char16_t (&str)[N], int64_t expected) { JS::BigInt* bi = JS::StringToBigInt(cx, mozilla::MakeStringSpan(str)); CHECK(bi); CHECK(JS::ToBigInt64(bi) == expected); return true; } template inline bool Fails(const char16_t (&str)[N]) { JS::BigInt* bi = JS::StringToBigInt(cx, mozilla::MakeStringSpan(str)); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); JS::ErrorReportBuilder report(cx); CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); CHECK(report.report()->exnType == JSEXN_SYNTAXERR); CHECK(report.report()->errorNumber == JSMSG_BIGINT_INVALID_SYNTAX); CHECK(!JS_IsExceptionPending(cx)); return true; } END_TEST(testStringToBigInt_AcceptedInput) BEGIN_TEST(testSimpleStringToBigInt_AcceptedInput) { CHECK(Allowed("12345", 10, 12345)); CHECK(Allowed("+12345", 10, 12345)); CHECK(Allowed("-12345", 10, -12345)); CHECK(Allowed("775", 8, 0775)); CHECK(Allowed("+775", 8, 0775)); CHECK(Allowed("-775", 8, -0775)); CHECK(Allowed("cAfE", 16, 0xCAFE)); CHECK(Allowed("+cAfE", 16, +0xCAFE)); CHECK(Allowed("-cAfE", 16, -0xCAFE)); CHECK(Allowed("-0", 10, 0)); CHECK(Fails("", 10)); CHECK(Fails("\n", 10)); CHECK(Fails(" ", 10)); CHECK(Fails("0\n", 10)); CHECK(Fails("0 ", 10)); CHECK(Fails("\n1", 10)); CHECK(Fails(" 1", 10)); CHECK(Fails("\n2 ", 10)); CHECK(Fails(" 2\n", 10)); CHECK(Fails("0b11", 2)); CHECK(Fails("0x17", 16)); CHECK(Fails("!!!!!!111one1111one1!1!1!!", 10)); CHECK(Fails("3.1416", 10)); CHECK(Fails("6.022e23", 10)); CHECK(Fails("1e3", 10)); CHECK(Fails(".25", 10)); CHECK(Fails(".25e2", 10)); CHECK(Fails("1_000_000", 10)); CHECK(Fails("3n", 10)); CHECK(Fails("-0x3", 10)); CHECK(Fails("Infinity", 10)); CHECK(Fails("555", 4)); CHECK(Fails("fff", 15)); return true; } template inline bool Allowed(const char (&str)[N], unsigned radix, int64_t expected) { JS::BigInt* bi = JS::SimpleStringToBigInt(cx, {str, N - 1}, radix); CHECK(bi); CHECK(JS::ToBigInt64(bi) == expected); return true; } template inline bool Fails(const char (&str)[N], unsigned radix) { JS::BigInt* bi = JS::SimpleStringToBigInt(cx, {str, N - 1}, radix); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); JS::ErrorReportBuilder report(cx); CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); CHECK(report.report()->exnType == JSEXN_SYNTAXERR); CHECK(report.report()->errorNumber == JSMSG_BIGINT_INVALID_SYNTAX); CHECK(!JS_IsExceptionPending(cx)); return true; } END_TEST(testSimpleStringToBigInt_AcceptedInput) BEGIN_TEST(testSimpleStringToBigInt_AllPossibleDigits) { const char allPossible[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; JS::BigInt* bi = JS::SimpleStringToBigInt(cx, mozilla::MakeStringSpan(allPossible), 36); CHECK(bi); JS::Rooted val(cx, JS::BigIntValue(bi)); JS::Rooted str(cx, JS::ToString(cx, val)); CHECK(str); // Answer calculated using Python: // int('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', 36) // Do not trust online base-36 calculators for values > UINT32_MAX! bool match; CHECK( JS_StringEqualsLiteral(cx, str, "8870050151210747660007771095260505028056221996735" "67534007158336222790086855213834764150805438340", &match)); CHECK(match); return true; } END_TEST(testSimpleStringToBigInt_AllPossibleDigits) BEGIN_TEST(testSimpleStringToBigInt_RadixOutOfRange) { CHECK(RadixOutOfRange(1)); CHECK(RadixOutOfRange(37)); return true; } inline bool RadixOutOfRange(unsigned radix) { JS::BigInt* bi = JS::SimpleStringToBigInt(cx, mozilla::MakeStringSpan("1"), radix); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); JS::ErrorReportBuilder report(cx); CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); CHECK(report.report()->exnType == JSEXN_RANGEERR); CHECK(report.report()->errorNumber == JSMSG_BAD_RADIX); CHECK(!JS_IsExceptionPending(cx)); return true; } END_TEST(testSimpleStringToBigInt_RadixOutOfRange) BEGIN_TEST(testToBigInt_Undefined) { JS::Rooted v(cx, JS::UndefinedValue()); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS_ClearPendingException(cx); return true; } END_TEST(testToBigInt_Undefined) BEGIN_TEST(testToBigInt_Null) { JS::Rooted v(cx, JS::NullValue()); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS_ClearPendingException(cx); return true; } END_TEST(testToBigInt_Null) BEGIN_TEST(testToBigInt_Boolean) { JS::Rooted v(cx, JS::TrueValue()); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK(bi); CHECK(JS::ToBigInt64(bi) == 1); v = JS::FalseValue(); bi = JS::ToBigInt(cx, v); CHECK(bi); CHECK(JS::ToBigInt64(bi) == 0); return true; } END_TEST(testToBigInt_Boolean) BEGIN_TEST(testToBigInt_BigInt) { JS::Rooted v(cx); EVAL("42n", &v); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK(bi); CHECK(JS::ToBigInt64(bi) == 42); return true; } END_TEST(testToBigInt_BigInt) BEGIN_TEST(testToBigInt_Number) { JS::Rooted v(cx, JS::Int32Value(42)); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS_ClearPendingException(cx); return true; } END_TEST(testToBigInt_Number) BEGIN_TEST(testToBigInt_String) { JS::Rooted v(cx); EVAL("'42'", &v); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK(bi); CHECK(JS::ToBigInt64(bi) == 42); return true; } END_TEST(testToBigInt_String) BEGIN_TEST(testToBigInt_Symbol) { JS::Rooted v(cx); EVAL("Symbol.toStringTag", &v); JS::BigInt* bi = JS::ToBigInt(cx, v); CHECK_NULL(bi); CHECK(JS_IsExceptionPending(cx)); JS_ClearPendingException(cx); return true; } END_TEST(testToBigInt_Symbol) BEGIN_TEST(testBigIntToNumber) { JS::BigInt* bi = JS::NumberToBigInt(cx, 0); CHECK(bi); int32_t result; CHECK(mozilla::NumberIsInt32(JS::BigIntToNumber(bi), &result)); CHECK_EQUAL(result, 0); bi = JS::NumberToBigInt(cx, 100); CHECK(bi); CHECK(JS::BigIntToNumber(bi) == 100); bi = JS::NumberToBigInt(cx, -100); CHECK(bi); CHECK(JS::BigIntToNumber(bi) == -100); JS::Rooted v(cx); EVAL("18446744073709551615n", &v); CHECK(v.isBigInt()); double numberValue = JS::BigIntToNumber(v.toBigInt()); EVAL("Number(18446744073709551615n)", &v); CHECK(v.isNumber()); CHECK(numberValue == v.toNumber()); EVAL((std::string(500, '9') + "n").c_str(), &v); CHECK(v.isBigInt()); CHECK(JS::BigIntToNumber(v.toBigInt()) == INFINITY); return true; } END_TEST(testBigIntToNumber) BEGIN_TEST(testBigIntIsNegative) { JS::BigInt* bi = JS::NumberToBigInt(cx, 0); CHECK(bi); CHECK(!JS::BigIntIsNegative(bi)); bi = JS::NumberToBigInt(cx, 100); CHECK(bi); CHECK(!JS::BigIntIsNegative(bi)); bi = JS::NumberToBigInt(cx, -100); CHECK(bi); CHECK(JS::BigIntIsNegative(bi)); return true; } END_TEST(testBigIntIsNegative) #define GENERATE_INTTYPE_TEST(bits) \ BEGIN_TEST(testBigIntFits_Int##bits) { \ int64_t in = INT##bits##_MIN; \ JS::BigInt* bi = JS::NumberToBigInt(cx, in); \ CHECK(bi); \ int##bits##_t i; \ CHECK(JS::BigIntFits(bi, &i)); \ CHECK_EQUAL(i, in); \ \ in = int64_t(INT##bits##_MIN) - 1; \ bi = JS::NumberToBigInt(cx, in); \ CHECK(bi); \ CHECK(!JS::BigIntFits(bi, &i)); \ \ in = INT64_MIN; \ bi = JS::NumberToBigInt(cx, in); \ CHECK(bi); \ CHECK(!JS::BigIntFits(bi, &i)); \ \ in = INT##bits##_MAX; \ bi = JS::NumberToBigInt(cx, in); \ CHECK(bi); \ CHECK(JS::BigIntFits(bi, &i)); \ CHECK_EQUAL(i, in); \ \ in = int64_t(INT##bits##_MAX) + 1; \ bi = JS::NumberToBigInt(cx, in); \ CHECK(bi); \ CHECK(!JS::BigIntFits(bi, &i)); \ \ in = INT64_MAX; \ bi = JS::NumberToBigInt(cx, in); \ CHECK(bi); \ CHECK(!JS::BigIntFits(bi, &i)); \ \ uint64_t uin = 0; \ bi = JS::NumberToBigInt(cx, uin); \ CHECK(bi); \ uint##bits##_t u; \ CHECK(JS::BigIntFits(bi, &u)); \ CHECK_EQUAL(u, uin); \ \ uin = UINT##bits##_MAX; \ bi = JS::NumberToBigInt(cx, uin); \ CHECK(bi); \ CHECK(JS::BigIntFits(bi, &u)); \ CHECK_EQUAL(u, uin); \ \ uin = uint64_t(UINT##bits##_MAX) + 1; \ bi = JS::NumberToBigInt(cx, uin); \ CHECK(bi); \ CHECK(!JS::BigIntFits(bi, &u)); \ \ uin = UINT64_MAX; \ bi = JS::NumberToBigInt(cx, uin); \ CHECK(bi); \ CHECK(!JS::BigIntFits(bi, &u)); \ \ return true; \ } \ END_TEST(testBigIntFits_Int##bits) GENERATE_INTTYPE_TEST(8); GENERATE_INTTYPE_TEST(16); GENERATE_INTTYPE_TEST(32); #undef GENERATE_INTTYPE_TEST BEGIN_TEST(testBigIntFits_Int64) { int64_t in = INT64_MIN; JS::BigInt* bi = JS::NumberToBigInt(cx, in); CHECK(bi); int64_t i; CHECK(JS::BigIntFits(bi, &i)); CHECK_EQUAL(i, in); in = INT64_MAX; bi = JS::NumberToBigInt(cx, in); CHECK(bi); CHECK(JS::BigIntFits(bi, &i)); CHECK_EQUAL(i, in); JS::RootedValue v(cx); EVAL((std::string(500, '9') + "n").c_str(), &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFits(v.toBigInt(), &i)); EVAL(("-" + std::string(500, '9') + "n").c_str(), &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFits(v.toBigInt(), &i)); return true; } END_TEST(testBigIntFits_Int64) BEGIN_TEST(testBigIntFits_Uint64) { uint64_t uin = 0; JS::BigInt* bi = JS::NumberToBigInt(cx, uin); CHECK(bi); uint64_t u; CHECK(JS::BigIntFits(bi, &u)); CHECK_EQUAL(u, uin); uin = UINT64_MAX; bi = JS::NumberToBigInt(cx, uin); CHECK(bi); CHECK(JS::BigIntFits(bi, &u)); CHECK_EQUAL(u, uin); JS::RootedValue v(cx); EVAL((std::string(500, '9') + "n").c_str(), &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFits(v.toBigInt(), &u)); return true; } END_TEST(testBigIntFits_Uint64) #define GENERATE_SIGNED_VALUE_TEST(type, tag, val) \ BEGIN_TEST(testBigIntFits_##type##_##tag) { \ int64_t v = val; \ JS::BigInt* bi = JS::NumberToBigInt(cx, v); \ CHECK(bi); \ type result; \ CHECK(JS::BigIntFits(bi, &result)); \ CHECK_EQUAL(v, result); \ return true; \ } \ END_TEST(testBigIntFits_##type##_##tag) #define GENERATE_UNSIGNED_VALUE_TEST(type, tag, val) \ BEGIN_TEST(testBigIntFits_##type##_##tag) { \ uint64_t v = val; \ JS::BigInt* bi = JS::NumberToBigInt(cx, v); \ CHECK(bi); \ type result; \ CHECK(JS::BigIntFits(bi, &result)); \ CHECK_EQUAL(v, result); \ return true; \ } \ END_TEST(testBigIntFits_##type##_##tag) GENERATE_SIGNED_VALUE_TEST(int, zero, 0); GENERATE_SIGNED_VALUE_TEST(int, aValue, -42); GENERATE_UNSIGNED_VALUE_TEST(unsigned, zero, 0); GENERATE_UNSIGNED_VALUE_TEST(unsigned, aValue, 42); GENERATE_SIGNED_VALUE_TEST(long, zero, 0); GENERATE_SIGNED_VALUE_TEST(long, aValue, -42); GENERATE_UNSIGNED_VALUE_TEST(uintptr_t, zero, 0); GENERATE_UNSIGNED_VALUE_TEST(uintptr_t, aValue, 42); GENERATE_UNSIGNED_VALUE_TEST(size_t, zero, 0); GENERATE_UNSIGNED_VALUE_TEST(size_t, aValue, 42); #undef GENERATE_SIGNED_VALUE_TEST #undef GENERATE_UNSIGNED_VALUE_TEST BEGIN_TEST(testBigIntFitsNumber) { JS::BigInt* bi = JS::NumberToBigInt(cx, 0); CHECK(bi); double num; CHECK(JS::BigIntFitsNumber(bi, &num)); int32_t result; CHECK(mozilla::NumberIsInt32(num, &result)); CHECK_EQUAL(result, 0); bi = JS::NumberToBigInt(cx, 100); CHECK(bi); CHECK(JS::BigIntFitsNumber(bi, &num)); CHECK(num == 100); bi = JS::NumberToBigInt(cx, -100); CHECK(bi); CHECK(JS::BigIntFitsNumber(bi, &num)); CHECK(num == -100); JS::Rooted v(cx); EVAL("BigInt(Number.MAX_SAFE_INTEGER)", &v); CHECK(v.isBigInt()); CHECK(JS::BigIntFitsNumber(v.toBigInt(), &num)); EVAL("BigInt(Number.MIN_SAFE_INTEGER)", &v); CHECK(v.isBigInt()); CHECK(JS::BigIntFitsNumber(v.toBigInt(), &num)); EVAL("BigInt(Number.MAX_SAFE_INTEGER) + 1n", &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFitsNumber(v.toBigInt(), &num)); EVAL("BigInt(Number.MIN_SAFE_INTEGER) - 1n", &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFitsNumber(v.toBigInt(), &num)); EVAL((std::string(500, '9') + "n").c_str(), &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFitsNumber(v.toBigInt(), &num)); EVAL(("-" + std::string(500, '9') + "n").c_str(), &v); CHECK(v.isBigInt()); CHECK(!JS::BigIntFitsNumber(v.toBigInt(), &num)); return true; } END_TEST(testBigIntFitsNumber) BEGIN_TEST(testBigIntToString) { CHECK(Convert(12345, 10, "12345")); CHECK(Convert(-12345, 10, "-12345")); CHECK(Convert(0775, 8, "775")); CHECK(Convert(-0775, 8, "-775")); CHECK(Convert(0xCAFE, 16, "cafe")); CHECK(Convert(-0xCAFE, 16, "-cafe")); return true; } template inline bool Convert(int64_t input, uint8_t radix, const char (&expected)[N]) { JS::Rooted bi(cx, JS::NumberToBigInt(cx, input)); CHECK(bi); JS::Rooted str(cx, JS::BigIntToString(cx, bi, radix)); CHECK(str); bool match; CHECK(JS_StringEqualsLiteral(cx, str, expected, &match)); CHECK(match); return true; } END_TEST(testBigIntToString) BEGIN_TEST(testBigIntToString_AllPossibleDigits) { const char allPossible[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; JS::Rooted bi( cx, JS::SimpleStringToBigInt(cx, mozilla::MakeStringSpan(allPossible), 36)); CHECK(bi); JS::Rooted str(cx, JS::BigIntToString(cx, bi, 36)); CHECK(str); bool match; CHECK(JS_StringEqualsLiteral(cx, str, "abcdefghijklmnopqrstuvwxyz1234567890", &match)); CHECK(match); return true; } END_TEST(testBigIntToString_AllPossibleDigits) BEGIN_TEST(testBigIntToString_RadixOutOfRange) { CHECK(RadixOutOfRange(1)); CHECK(RadixOutOfRange(37)); return true; } inline bool RadixOutOfRange(uint8_t radix) { JS::Rooted bi(cx, JS::NumberToBigInt(cx, 1)); CHECK(bi); JSString* s = JS::BigIntToString(cx, bi, radix); CHECK_NULL(s); CHECK(JS_IsExceptionPending(cx)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); JS::ErrorReportBuilder report(cx); CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); CHECK(report.report()->exnType == JSEXN_RANGEERR); CHECK(report.report()->errorNumber == JSMSG_BAD_RADIX); CHECK(!JS_IsExceptionPending(cx)); return true; } END_TEST(testBigIntToString_RadixOutOfRange)