[bigint] Fix accidental input modification in Divide

"AbsoluteDivSmall" had a shortcut path for abs(divisor) == 1 where
it would simply return the dividend as result. However, its caller
"Divide" was blissfully ignorant of this trick and would therefore
simply set the value's sign as needed, modifying the input.
This CL prevents that, while continuing to avoid the full division
algorithm for abs(divisor) == 1.

Bug: v8:6791
Change-Id: I04cdc93f5ed2a696587c35c754e68f07012dd1a9
Reviewed-on: https://chromium-review.googlesource.com/772332
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49433}
This commit is contained in:
Jakob Kummerow 2017-11-16 17:22:13 -08:00 committed by Commit Bot
parent 7e9448edbc
commit b5997de8a9
2 changed files with 32 additions and 11 deletions

View File

@ -81,9 +81,14 @@ MaybeHandle<BigInt> BigInt::Divide(Handle<BigInt> x, Handle<BigInt> y) {
return x->GetIsolate()->factory()->NewBigIntFromInt(0);
}
Handle<BigInt> quotient;
bool result_sign = x->sign() != y->sign();
if (y->length() == 1) {
digit_t divisor = y->digit(0);
if (divisor == 1) {
return result_sign == x->sign() ? x : UnaryMinus(x);
}
digit_t remainder;
AbsoluteDivSmall(x, y->digit(0), &quotient, &remainder);
AbsoluteDivSmall(x, divisor, &quotient, &remainder);
} else {
AbsoluteDivLarge(x, y, &quotient, nullptr);
}
@ -93,22 +98,25 @@ MaybeHandle<BigInt> BigInt::Divide(Handle<BigInt> x, Handle<BigInt> y) {
}
MaybeHandle<BigInt> BigInt::Remainder(Handle<BigInt> x, Handle<BigInt> y) {
Isolate* isolate = x->GetIsolate();
// 1. If y is 0n, throw a RangeError exception.
if (y->is_zero()) {
THROW_NEW_ERROR(y->GetIsolate(),
NewRangeError(MessageTemplate::kBigIntDivZero), BigInt);
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntDivZero),
BigInt);
}
// 2. Return the BigInt representing x modulo y.
// See https://github.com/tc39/proposal-bigint/issues/84 though.
if (AbsoluteCompare(x, y) < 0) return x;
Handle<BigInt> remainder;
if (y->length() == 1) {
digit_t divisor = y->digit(0);
if (divisor == 1) return isolate->factory()->NewBigIntFromInt(0);
digit_t remainder_digit;
AbsoluteDivSmall(x, y->digit(0), nullptr, &remainder_digit);
AbsoluteDivSmall(x, divisor, nullptr, &remainder_digit);
if (remainder_digit == 0) {
return x->GetIsolate()->factory()->NewBigIntFromInt(0);
return isolate->factory()->NewBigIntFromInt(0);
}
remainder = x->GetIsolate()->factory()->NewBigIntRaw(1);
remainder = isolate->factory()->NewBigIntRaw(1);
remainder->set_digit(0, remainder_digit);
} else {
AbsoluteDivLarge(x, y, nullptr, &remainder);
@ -1035,11 +1043,6 @@ void BigInt::AbsoluteDivSmall(Handle<BigInt> x, digit_t divisor,
DCHECK_NE(divisor, 0);
DCHECK(!x->is_zero()); // Callers check anyway, no need to handle this.
*remainder = 0;
if (divisor == 1) {
if (quotient != nullptr) *quotient = x;
return;
}
int length = x->length();
if (quotient != nullptr) {
if ((*quotient).is_null()) {

View File

@ -0,0 +1,18 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-bigint --noopt
var a = 5n;
var b = a / -1n;
assertEquals(5n, a);
assertEquals(-5n, b);
assertEquals(5n, 5n / 1n);
assertEquals(5n, -5n / -1n);
assertEquals(-5n, -5n / 1n);
assertEquals(0n, 5n % 1n);
assertEquals(0n, -5n % 1n);
assertEquals(0n, 5n % -1n);
assertEquals(0n, -5n % -1n);