зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1606568) for estBigInt.cpp related bustage and BigIntType.cpp related hazard CLOSED TREE
Backed out changeset 835cceddaf49 (bug 1606568) Backed out changeset 747640138ffb (bug 1606568) Backed out changeset 7890c93298ec (bug 1606568)
This commit is contained in:
Родитель
d1195b0984
Коммит
550fcde1da
|
@ -1,139 +0,0 @@
|
||||||
/* -*- 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 */
|
|
|
@ -40,20 +40,6 @@ class Latin1Chars : public mozilla::Range<Latin1Char> {
|
||||||
aLength) {}
|
aLength) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Like Latin1Chars, but the chars are const.
|
|
||||||
*/
|
|
||||||
class ConstLatin1Chars : public mozilla::Range<const Latin1Char> {
|
|
||||||
typedef mozilla::Range<const Latin1Char> Base;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using CharT = Latin1Char;
|
|
||||||
|
|
||||||
ConstLatin1Chars() = default;
|
|
||||||
ConstLatin1Chars(const Latin1Char* aChars, size_t aLength)
|
|
||||||
: Base(aChars, aLength) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A Latin1Chars, but with \0 termination for C compatibility.
|
* A Latin1Chars, but with \0 termination for C compatibility.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,7 +18,6 @@ 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',
|
||||||
|
|
|
@ -1,441 +0,0 @@
|
||||||
/* 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,7 +126,6 @@ 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,21 +86,18 @@
|
||||||
#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>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits> // std::is_same_v
|
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
|
|
||||||
#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"
|
||||||
|
@ -1557,11 +1554,6 @@ BigInt* BigInt::parseLiteralDigits(JSContext* cx,
|
||||||
const Range<const CharT> chars,
|
const Range<const CharT> chars,
|
||||||
unsigned radix, bool isNegative,
|
unsigned radix, bool isNegative,
|
||||||
bool* haveParseError, gc::InitialHeap heap) {
|
bool* haveParseError, gc::InitialHeap heap) {
|
||||||
static_assert(
|
|
||||||
std::is_same_v<CharT, JS::Latin1Char> || std::is_same_v<CharT, char16_t>,
|
|
||||||
"only the bare minimum character types are supported, to avoid "
|
|
||||||
"excessively instantiating this template");
|
|
||||||
|
|
||||||
MOZ_ASSERT(chars.length());
|
MOZ_ASSERT(chars.length());
|
||||||
|
|
||||||
RangedPtr<const CharT> start = chars.begin();
|
RangedPtr<const CharT> start = chars.begin();
|
||||||
|
@ -2965,7 +2957,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 = js::ToBigInt(cx, v);
|
BigInt* bi = ToBigInt(cx, v);
|
||||||
if (!bi) {
|
if (!bi) {
|
||||||
return cx->alreadyReportedError();
|
return cx->alreadyReportedError();
|
||||||
}
|
}
|
||||||
|
@ -2973,7 +2965,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 = js::ToBigInt(cx, v);
|
BigInt* bi = ToBigInt(cx, v);
|
||||||
if (!bi) {
|
if (!bi) {
|
||||||
return cx->alreadyReportedError();
|
return cx->alreadyReportedError();
|
||||||
}
|
}
|
||||||
|
@ -3727,95 +3719,3 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче