summaryrefslogtreecommitdiffstats
path: root/js/src/jsapi-tests/testBigInt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsapi-tests/testBigInt.cpp')
-rw-r--r--js/src/jsapi-tests/testBigInt.cpp768
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)