Source code

Revision control

Copy as Markdown

Other Tools

/* 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)