diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jsapi-tests/testBigInt.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jsapi-tests/testBigInt.cpp')
-rw-r--r-- | js/src/jsapi-tests/testBigInt.cpp | 768 |
1 files changed, 768 insertions, 0 deletions
diff --git a/js/src/jsapi-tests/testBigInt.cpp b/js/src/jsapi-tests/testBigInt.cpp new file mode 100644 index 0000000000..1a1dbfe4ec --- /dev/null +++ b/js/src/jsapi-tests/testBigInt.cpp @@ -0,0 +1,768 @@ +/* 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 <stdint.h> + +#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<JS::Value> 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<JS::Value> 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<const char16_t> input{ + mozilla::MakeStringSpan(u"18446744073709551616")}; + JS::BigInt* bi = JS::StringToBigInt(cx, input); + CHECK(bi); + JS::Rooted<JS::Value> val(cx, JS::BigIntValue(bi)); + JS::Rooted<JSString*> 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 <size_t N> +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 <size_t N> +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 <size_t N> +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 <size_t N> +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<JS::Value> val(cx, JS::BigIntValue(bi)); + JS::Rooted<JSString*> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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 <size_t N> +inline bool Convert(int64_t input, uint8_t radix, const char (&expected)[N]) { + JS::Rooted<JS::BigInt*> bi(cx, JS::NumberToBigInt(cx, input)); + CHECK(bi); + JS::Rooted<JSString*> 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<JS::BigInt*> bi( + cx, + JS::SimpleStringToBigInt(cx, mozilla::MakeStringSpan(allPossible), 36)); + CHECK(bi); + JS::Rooted<JSString*> 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<JS::BigInt*> 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) |