Commit 207bb037 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Allow BigInt(n) for n > MAX_SAFE_INTEGER

Spec change: https://github.com/tc39/proposal-bigint/pull/138

Bug: v8:6791
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I7367273ed1e98971be3b277f6486333a96412185
Reviewed-on: https://chromium-review.googlesource.com/1004120
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52611}
parent 9367f80f
......@@ -280,8 +280,8 @@ class ErrorUtils : public AllStatic {
T(BadSortComparisonFunction, \
"The comparison function must be either a function or undefined") \
T(BigIntFromNumber, \
"The number % is not a safe integer and thus cannot be converted to a " \
"BigInt") \
"The number % cannot be converted to a BigInt because it is not an " \
"integer") \
T(BigIntFromObject, "Cannot convert % to a BigInt") \
T(BigIntMixedTypes, \
"Cannot mix BigInt and other types, use explicit conversions") \
......
......@@ -43,7 +43,7 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
static MaybeHandle<MutableBigInt> New(Isolate* isolate, int length,
PretenureFlag pretenure = NOT_TENURED);
static Handle<BigInt> NewFromInt(Isolate* isolate, int value);
static Handle<BigInt> NewFromSafeInteger(Isolate* isolate, double value);
static Handle<BigInt> NewFromDouble(Isolate* isolate, double value);
void InitializeDigits(int length, byte value = 0);
static Handle<MutableBigInt> Copy(Handle<BigIntBase> source);
static Handle<BigInt> Zero(Isolate* isolate) {
......@@ -220,16 +220,70 @@ Handle<BigInt> MutableBigInt::NewFromInt(Isolate* isolate, int value) {
return MakeImmutable(result);
}
Handle<BigInt> MutableBigInt::NewFromSafeInteger(Isolate* isolate,
double value) {
Handle<BigInt> MutableBigInt::NewFromDouble(Isolate* isolate, double value) {
DCHECK_EQ(value, std::floor(value));
if (value == 0) return Zero(isolate);
uint64_t absolute = std::abs(value);
int length = 64 / kDigitBits;
Handle<MutableBigInt> result = Cast(isolate->factory()->NewBigInt(length));
bool sign = value < 0; // Treats -0 like 0.
result->initialize_bitfield(sign, length);
result->set_64_bits(absolute);
bool sign = value < 0; // -0 was already handled above.
uint64_t double_bits = bit_cast<uint64_t>(value);
int raw_exponent =
static_cast<int>(double_bits >> Double::kPhysicalSignificandSize) & 0x7FF;
DCHECK_NE(raw_exponent, 0x7FF);
DCHECK_GE(raw_exponent, 0x3FF);
int exponent = raw_exponent - 0x3FF;
int digits = exponent / kDigitBits + 1;
Handle<MutableBigInt> result = Cast(isolate->factory()->NewBigInt(digits));
result->initialize_bitfield(sign, digits);
// We construct a BigInt from the double {value} by shifting its mantissa
// according to its exponent and mapping the bit pattern onto digits.
//
// <----------- bitlength = exponent + 1 ----------->
// <----- 52 ------> <------ trailing zeroes ------>
// mantissa: 1yyyyyyyyyyyyyyyyy 0000000000000000000000000000000
// digits: 0001xxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
// <--> <------>
// msd_topbit kDigitBits
//
uint64_t mantissa =
(double_bits & Double::kSignificandMask) | Double::kHiddenBit;
const int kMantissaTopBit = Double::kSignificandSize - 1; // 0-indexed.
// 0-indexed position of most significant bit in the most significant digit.
int msd_topbit = exponent % kDigitBits;
// Number of unused bits in {mantissa}. We'll keep them shifted to the
// left (i.e. most significant part) of the underlying uint64_t.
int remaining_mantissa_bits = 0;
// Next digit under construction.
digit_t digit;
// First, build the MSD by shifting the mantissa appropriately.
if (msd_topbit < kMantissaTopBit) {
remaining_mantissa_bits = kMantissaTopBit - msd_topbit;
digit = mantissa >> remaining_mantissa_bits;
mantissa = mantissa << (64 - remaining_mantissa_bits);
} else {
DCHECK_GE(msd_topbit, kMantissaTopBit);
digit = mantissa << (msd_topbit - kMantissaTopBit);
mantissa = 0;
}
result->set_digit(digits - 1, digit);
// Then fill in the rest of the digits.
for (int digit_index = digits - 2; digit_index >= 0; digit_index--) {
if (remaining_mantissa_bits > 0) {
remaining_mantissa_bits -= kDigitBits;
if (sizeof(digit) == 4) {
digit = mantissa >> 32;
mantissa = mantissa << 32;
} else {
DCHECK_EQ(sizeof(digit), 8);
digit = mantissa;
mantissa = 0;
}
} else {
digit = 0;
}
result->set_digit(digit_index, digit);
}
return MakeImmutable(result);
}
......@@ -850,33 +904,19 @@ MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
return MutableBigInt::ToStringGeneric(bigint, radix);
}
namespace {
bool IsSafeInteger(double value) {
if (std::isnan(value) || std::isinf(value)) return false;
// Let integer be ! ToInteger(value).
// If ! SameValueZero(integer, value) is false, return false.
if (DoubleToInteger(value) != value) return false;
return std::abs(value) <= kMaxSafeInteger;
}
} // anonymous namespace
MaybeHandle<BigInt> BigInt::FromNumber(Isolate* isolate,
Handle<Object> number) {
DCHECK(number->IsNumber());
if (number->IsSmi()) {
return MutableBigInt::NewFromInt(isolate, Smi::ToInt(*number));
}
if (!IsSafeInteger(Handle<HeapNumber>::cast(number)->value())) {
double value = HeapNumber::cast(*number)->value();
if (!std::isfinite(value) || (DoubleToInteger(value) != value)) {
THROW_NEW_ERROR(isolate,
NewRangeError(MessageTemplate::kBigIntFromNumber, number),
BigInt);
}
return MutableBigInt::NewFromSafeInteger(
isolate, Handle<HeapNumber>::cast(number)->value());
return MutableBigInt::NewFromDouble(isolate, value);
}
MaybeHandle<BigInt> BigInt::FromObject(Isolate* isolate, Handle<Object> obj) {
......
......@@ -56,6 +56,10 @@ const six = BigInt(6);
assertSame(BigInt(42n), 42n);
assertSame(BigInt(Object(42n)), 42n);
assertSame(BigInt(2**53 - 1), 9007199254740991n);
assertSame(BigInt(2**53), 9007199254740992n);
assertSame(BigInt(2**1000), 2n ** 1000n);
assertSame(BigInt(3.0755851989071915e29), 307558519890719151276406341632n);
assertSame(BigInt(-1e50), -0x446c3b15f992680000000000000000000000000000n);
assertSame(BigInt(Object(2**53 - 1)), 9007199254740991n);
assertSame(BigInt([]), 0n);
}{
......@@ -64,8 +68,6 @@ const six = BigInt(6);
assertThrows(() => BigInt(+Infinity), RangeError);
assertThrows(() => BigInt(4.00000001), RangeError);
assertThrows(() => BigInt(Object(4.00000001)), RangeError);
assertThrows(() => BigInt(2**53), RangeError);
assertThrows(() => BigInt(2**1000), RangeError);
}
// BigInt.prototype[Symbol.toStringTag]
......
......@@ -416,6 +416,10 @@
'built-ins/BigInt/prototype/Symbol.toStringTag': [FAIL],
'built-ins/TypedArrays/ctors-bigint/typedarray-arg/typedarray-arg-other-ctor-returns-new-typedarray': [FAIL],
# https://github.com/tc39/proposal-bigint/pull/139
'built-ins/BigInt/out-of-bounds-integer-rangeerror': [FAIL],
'built-ins/DataView/prototype/setBigInt64/set-values-return-undefined': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=7184
'annexB/language/expressions/yield/star-iterable-return-emulates-undefined-throws-when-called': [FAIL],
'annexB/language/statements/for-await-of/iterator-close-return-emulates-undefined-throws-when-called': [FAIL],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment