зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1606568 - Add public BigInt API. r=jwalden
Differential Revision: https://phabricator.services.mozilla.com/D82480
This commit is contained in:
Родитель
602751eaef
Коммит
d1195b0984
|
@ -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',
|
'testArrayBufferWithUserOwnedContents.cpp',
|
||||||
'testAtomicOperations.cpp',
|
'testAtomicOperations.cpp',
|
||||||
'testAtomizeUtf8NonAsciiLatin1CodePoint.cpp',
|
'testAtomizeUtf8NonAsciiLatin1CodePoint.cpp',
|
||||||
|
'testBigInt.cpp',
|
||||||
'testBoundFunction.cpp',
|
'testBoundFunction.cpp',
|
||||||
'testBug604087.cpp',
|
'testBug604087.cpp',
|
||||||
'testCallArgs.cpp',
|
'testCallArgs.cpp',
|
||||||
|
|
|
@ -0,0 +1,441 @@
|
||||||
|
/* 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 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/Array.h',
|
||||||
'../public/ArrayBuffer.h',
|
'../public/ArrayBuffer.h',
|
||||||
'../public/ArrayBufferMaybeShared.h',
|
'../public/ArrayBufferMaybeShared.h',
|
||||||
|
'../public/BigInt.h',
|
||||||
'../public/BuildId.h',
|
'../public/BuildId.h',
|
||||||
'../public/CallArgs.h',
|
'../public/CallArgs.h',
|
||||||
'../public/CallNonGenericMethod.h',
|
'../public/CallNonGenericMethod.h',
|
||||||
|
|
|
@ -86,6 +86,7 @@
|
||||||
#include "mozilla/MemoryChecking.h"
|
#include "mozilla/MemoryChecking.h"
|
||||||
#include "mozilla/Range.h"
|
#include "mozilla/Range.h"
|
||||||
#include "mozilla/RangedPtr.h"
|
#include "mozilla/RangedPtr.h"
|
||||||
|
#include "mozilla/Span.h" // mozilla::Span
|
||||||
#include "mozilla/WrappingOperations.h"
|
#include "mozilla/WrappingOperations.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -99,6 +100,7 @@
|
||||||
|
|
||||||
#include "builtin/BigInt.h"
|
#include "builtin/BigInt.h"
|
||||||
#include "gc/Allocator.h"
|
#include "gc/Allocator.h"
|
||||||
|
#include "js/BigInt.h"
|
||||||
#include "js/Conversions.h"
|
#include "js/Conversions.h"
|
||||||
#include "js/Initialization.h"
|
#include "js/Initialization.h"
|
||||||
#include "js/StableStringChars.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) {
|
JS::Result<int64_t> js::ToBigInt64(JSContext* cx, HandleValue v) {
|
||||||
BigInt* bi = ToBigInt(cx, v);
|
BigInt* bi = js::ToBigInt(cx, v);
|
||||||
if (!bi) {
|
if (!bi) {
|
||||||
return cx->alreadyReportedError();
|
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) {
|
JS::Result<uint64_t> js::ToBigUint64(JSContext* cx, HandleValue v) {
|
||||||
BigInt* bi = ToBigInt(cx, v);
|
BigInt* bi = js::ToBigInt(cx, v);
|
||||||
if (!bi) {
|
if (!bi) {
|
||||||
return cx->alreadyReportedError();
|
return cx->alreadyReportedError();
|
||||||
}
|
}
|
||||||
|
@ -3725,3 +3727,95 @@ template XDRResult js::XDRBigInt(XDRState<XDR_ENCODE>* xdr,
|
||||||
|
|
||||||
template XDRResult js::XDRBigInt(XDRState<XDR_DECODE>* xdr,
|
template XDRResult js::XDRBigInt(XDRState<XDR_DECODE>* xdr,
|
||||||
MutableHandleBigInt bi);
|
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;
|
||||||
|
BigInt* bi = ParseStringBigIntLiteral(cx, chars, &parseError);
|
||||||
|
if (parseError) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||||
|
JSMSG_BIGINT_INVALID_SYNTAX);
|
||||||
|
}
|
||||||
|
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 (haveParseError) {
|
||||||
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||||
|
JSMSG_BIGINT_INVALID_SYNTAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче