Bug 1606568 - Add public BigInt API. r=jwalden

Differential Revision: https://phabricator.services.mozilla.com/D82480
This commit is contained in:
Philip Chimento 2020-07-22 02:53:06 +00:00
Родитель 3a69560ad3
Коммит c3f0feda44
5 изменённых файлов: 686 добавлений и 2 удалений

139
js/public/BigInt.h Normal file
Просмотреть файл

@ -0,0 +1,139 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* BigInt. */
#ifndef js_BigInt_h
#define js_BigInt_h
#include "mozilla/Range.h" // mozilla::Range
#include <limits> // std::numeric_limits
#include <stdint.h> // int64_t, uint64_t
#include <type_traits> // std::enable_if_t, std::{true,false}_type, std::is_{integral,signed,unsigned}_v
#include <utility> // std::declval
#include "jstypes.h" // JS_PUBLIC_API
#include "js/RootingAPI.h" // JS::Handle
#include "js/Value.h" // JS::Value
struct JS_PUBLIC_API JSContext;
namespace JS {
class JS_PUBLIC_API BigInt;
namespace detail {
extern JS_PUBLIC_API BigInt* BigIntFromInt64(JSContext* cx, int64_t num);
extern JS_PUBLIC_API BigInt* BigIntFromUint64(JSContext* cx, uint64_t num);
extern JS_PUBLIC_API BigInt* BigIntFromBool(JSContext* cx, bool b);
template <typename T, typename = void>
struct NumberToBigIntConverter;
template <typename SignedIntT>
struct NumberToBigIntConverter<
SignedIntT,
std::enable_if_t<std::is_integral_v<SignedIntT> &&
std::is_signed_v<SignedIntT> &&
std::numeric_limits<SignedIntT>::digits <= 64>> {
static BigInt* convert(JSContext* cx, SignedIntT num) {
return BigIntFromInt64(cx, num);
}
};
template <typename UnsignedIntT>
struct NumberToBigIntConverter<
UnsignedIntT,
std::enable_if_t<std::is_integral_v<UnsignedIntT> &&
std::is_unsigned_v<UnsignedIntT> &&
std::numeric_limits<UnsignedIntT>::digits <= 64>> {
static BigInt* convert(JSContext* cx, UnsignedIntT num) {
return BigIntFromUint64(cx, num);
}
};
template <>
struct NumberToBigIntConverter<bool> {
static BigInt* convert(JSContext* cx, bool b) {
return BigIntFromBool(cx, b);
}
};
} // namespace detail
/**
* Create a BigInt from an integer value. All integral types not larger than 64
* bits in size are supported.
*/
template <typename NumericT>
extern JS_PUBLIC_API BigInt* NumberToBigInt(JSContext* cx, NumericT val) {
return detail::NumberToBigIntConverter<NumericT>::convert(cx, val);
}
/**
* Create a BigInt from a floating-point value. If the number isn't integral
* (that is, if it's NaN, an infinity, or contains a fractional component),
* this function returns null and throws an exception.
*
* Passing -0.0 will produce the bigint 0n.
*/
extern JS_PUBLIC_API BigInt* NumberToBigInt(JSContext* cx, double num);
/**
* Create a BigInt by parsing a string using the ECMAScript StringToBigInt
* algorithm (https://tc39.es/ecma262/#sec-stringtobigint). Latin1 and two-byte
* character ranges are supported. It may be convenient to use
* JS::ConstLatin1Chars or JS::ConstTwoByteChars.
*
* (StringToBigInt performs parsing similar to that performed by the |Number|
* global function when passed a string, but it doesn't allow infinities,
* decimal points, or exponential notation, and neither algorithm allows numeric
* separators or an 'n' suffix character. This fast-and-loose description is
* offered purely as a convenience to the reader: see the specification
* algorithm for exact behavior.)
*
* If parsing fails, this function returns null and throws an exception.
*/
extern JS_PUBLIC_API BigInt* StringToBigInt(
JSContext* cx, mozilla::Range<const Latin1Char> chars);
extern JS_PUBLIC_API BigInt* StringToBigInt(
JSContext* cx, mozilla::Range<const char16_t> chars);
/**
* Create a BigInt by parsing a string consisting of an optional sign character
* followed by one or more alphanumeric ASCII digits in the provided radix.
*
* If the radix is not in the range [2, 36], or the string fails to parse, this
* function returns null and throws an exception.
*/
extern JS_PUBLIC_API BigInt* SimpleStringToBigInt(
JSContext* cx, mozilla::Span<const char> chars, unsigned radix);
/**
* Convert a JS::Value to a BigInt using the ECMAScript ToBigInt algorithm
* (https://tc39.es/ecma262/#sec-tobigint).
*
* (Note in particular that this will throw if passed a value whose type is
* 'number'. To convert a number to a BigInt, use one of the overloads of
* JS::NumberToBigInt().)
*/
extern JS_PUBLIC_API BigInt* ToBigInt(JSContext* cx, Handle<Value> val);
/**
* Convert the given BigInt, modulo 2**64, to a signed 64-bit integer.
*/
extern JS_PUBLIC_API int64_t ToBigInt64(BigInt* bi);
/**
* Convert the given BigInt, modulo 2**64, to an unsigned 64-bit integer.
*/
extern JS_PUBLIC_API uint64_t ToBigUint64(BigInt* bi);
} // namespace JS
#endif /* js_BigInt_h */

Просмотреть файл

@ -18,6 +18,7 @@ UNIFIED_SOURCES += [
'testArrayBufferWithUserOwnedContents.cpp',
'testAtomicOperations.cpp',
'testAtomizeUtf8NonAsciiLatin1CodePoint.cpp',
'testBigInt.cpp',
'testBoundFunction.cpp',
'testBug604087.cpp',
'testCallArgs.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 "jsfriendapi.h" // JSMSG_BIGINT_INVALID_SYNTAX
#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/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)

Просмотреть файл

@ -126,6 +126,7 @@ EXPORTS.js += [
'../public/Array.h',
'../public/ArrayBuffer.h',
'../public/ArrayBufferMaybeShared.h',
'../public/BigInt.h',
'../public/BuildId.h',
'../public/CallArgs.h',
'../public/CallNonGenericMethod.h',

Просмотреть файл

@ -86,6 +86,7 @@
#include "mozilla/MemoryChecking.h"
#include "mozilla/Range.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/Span.h" // mozilla::Span
#include "mozilla/WrappingOperations.h"
#include <functional>
@ -99,6 +100,7 @@
#include "builtin/BigInt.h"
#include "gc/Allocator.h"
#include "js/BigInt.h"
#include "js/Conversions.h"
#include "js/Initialization.h"
#include "js/StableStringChars.h"
@ -2963,7 +2965,7 @@ BigInt* js::ToBigInt(JSContext* cx, HandleValue val) {
}
JS::Result<int64_t> js::ToBigInt64(JSContext* cx, HandleValue v) {
BigInt* bi = ToBigInt(cx, v);
BigInt* bi = js::ToBigInt(cx, v);
if (!bi) {
return cx->alreadyReportedError();
}
@ -2971,7 +2973,7 @@ JS::Result<int64_t> js::ToBigInt64(JSContext* cx, HandleValue v) {
}
JS::Result<uint64_t> js::ToBigUint64(JSContext* cx, HandleValue v) {
BigInt* bi = ToBigInt(cx, v);
BigInt* bi = js::ToBigInt(cx, v);
if (!bi) {
return cx->alreadyReportedError();
}
@ -3725,3 +3727,102 @@ template XDRResult js::XDRBigInt(XDRState<XDR_ENCODE>* xdr,
template XDRResult js::XDRBigInt(XDRState<XDR_DECODE>* xdr,
MutableHandleBigInt bi);
// Public API
BigInt* JS::NumberToBigInt(JSContext* cx, double num) {
return js::NumberToBigInt(cx, num);
}
template <typename CharT>
static inline BigInt* StringToBigIntHelper(JSContext* cx,
Range<const CharT>& chars) {
bool parseError = false;
BigInt* bi = ParseStringBigIntLiteral(cx, chars, &parseError);
if (!bi) {
if (parseError) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_BIGINT_INVALID_SYNTAX);
}
return nullptr;
}
MOZ_RELEASE_ASSERT(!parseError);
return bi;
}
BigInt* JS::StringToBigInt(JSContext* cx, Range<const Latin1Char> chars) {
return StringToBigIntHelper(cx, chars);
}
BigInt* JS::StringToBigInt(JSContext* cx, Range<const char16_t> chars) {
return StringToBigIntHelper(cx, chars);
}
static inline BigInt* SimpleStringToBigIntHelper(
JSContext* cx, mozilla::Span<const Latin1Char> chars, unsigned radix,
bool* haveParseError) {
if (chars.Length() > 1) {
if (chars[0] == '+') {
return BigInt::parseLiteralDigits(
cx, Range<const Latin1Char>{chars.From(1)}, radix,
/* isNegative = */ false, haveParseError);
}
if (chars[0] == '-') {
return BigInt::parseLiteralDigits(
cx, Range<const Latin1Char>{chars.From(1)}, radix,
/* isNegative = */ true, haveParseError);
}
}
return BigInt::parseLiteralDigits(cx, Range<const Latin1Char>{chars}, radix,
/* isNegative = */ false, haveParseError);
}
BigInt* JS::SimpleStringToBigInt(JSContext* cx, mozilla::Span<const char> chars,
unsigned radix) {
if (chars.empty()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_BIGINT_INVALID_SYNTAX);
return nullptr;
}
if (radix < 2 || radix > 36) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_RADIX);
return nullptr;
}
mozilla::Span<const Latin1Char> latin1{
reinterpret_cast<const Latin1Char*>(chars.data()), chars.size()};
bool haveParseError = false;
BigInt* bi = SimpleStringToBigIntHelper(cx, latin1, radix, &haveParseError);
if (!bi) {
if (haveParseError) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_BIGINT_INVALID_SYNTAX);
}
return nullptr;
}
MOZ_RELEASE_ASSERT(!haveParseError);
return bi;
}
BigInt* JS::ToBigInt(JSContext* cx, HandleValue val) {
return js::ToBigInt(cx, val);
}
int64_t JS::ToBigInt64(JS::BigInt* bi) { return BigInt::toInt64(bi); }
uint64_t JS::ToBigUint64(JS::BigInt* bi) { return BigInt::toUint64(bi); }
// Semi-public template details
BigInt* JS::detail::BigIntFromInt64(JSContext* cx, int64_t num) {
return BigInt::createFromInt64(cx, num);
}
BigInt* JS::detail::BigIntFromUint64(JSContext* cx, uint64_t num) {
return BigInt::createFromUint64(cx, num);
}
BigInt* JS::detail::BigIntFromBool(JSContext* cx, bool b) {
return b ? BigInt::one(cx) : BigInt::zero(cx);
}