diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/jsapi-tests/testBigInt.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
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 | 442 |
1 files changed, 442 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..f2a1a460b6 --- /dev/null +++ b/js/src/jsapi-tests/testBigInt.cpp @@ -0,0 +1,442 @@ +/* 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/Range.h" // mozilla::Range +#include "mozilla/Span.h" // mozilla::MakeStringSpan + +#include <stdint.h> + +#include "jsapi.h" // JS_IsExceptionPending, JS_StringEqualsLiteral + +#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 +#include "js/friend/ErrorMessages.h" // JSMSG_* +#include "js/RootingAPI.h" // JS::Rooted +#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) |