Commit 8cc5a723 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Update "bigint < string" semantics

Per the spec change at [1], Abstract Relational Comparison between a
BigInt and a String converts the String to BigInt via StringToBigInt
before performing the comparison. Before this change, the String was
converted to a Number, and a BigInt/Number comparison was performed.

[1] https://github.com/tc39/proposal-bigint/pull/139

Bug: v8:6791
Change-Id: I40b4f4ddc78977adb0d44180eb58e0f9a8a70cb6
Reviewed-on: https://chromium-review.googlesource.com/1004117
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52609}
parent 88b08f12
......@@ -9166,11 +9166,12 @@ Node* CodeStubAssembler::RelationalComparison(Operation op, Node* left,
BIND(&if_left_bigint);
{
Label if_right_heapnumber(this), if_right_bigint(this),
if_right_not_numeric(this);
if_right_string(this), if_right_other(this);
GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber);
Node* right_instance_type = LoadMapInstanceType(right_map);
Branch(IsBigIntInstanceType(right_instance_type), &if_right_bigint,
&if_right_not_numeric);
GotoIf(IsBigIntInstanceType(right_instance_type), &if_right_bigint);
Branch(IsStringInstanceType(right_instance_type), &if_right_string,
&if_right_other);
BIND(&if_right_heapnumber);
{
......@@ -9192,7 +9193,18 @@ Node* CodeStubAssembler::RelationalComparison(Operation op, Node* left,
Goto(&end);
}
BIND(&if_right_not_numeric);
BIND(&if_right_string);
{
OverwriteFeedback(var_type_feedback,
CompareOperationFeedback::kAny);
var_result = CAST(CallRuntime(Runtime::kBigIntCompareToString,
NoContextConstant(), SmiConstant(op),
left, right));
Goto(&end);
}
// {right} is not a Number, BigInt, or String.
BIND(&if_right_other);
{
OverwriteFeedback(var_type_feedback,
CompareOperationFeedback::kAny);
......@@ -9240,11 +9252,14 @@ Node* CodeStubAssembler::RelationalComparison(Operation op, Node* left,
{
OverwriteFeedback(var_type_feedback,
CompareOperationFeedback::kAny);
// {left} is a String, while {right} isn't. So we call
// ToPrimitive(right, hint Number) if {right} is a receiver, or
// ToNumeric(left) and then ToNumeric(right) in the other cases.
// {left} is a String, while {right} isn't. Check if {right} is
// a BigInt, otherwise call ToPrimitive(right, hint Number) if
// {right} is a receiver, or ToNumeric(left) and then
// ToNumeric(right) in the other cases.
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Label if_right_receiver(this, Label::kDeferred);
Label if_right_bigint(this),
if_right_receiver(this, Label::kDeferred);
GotoIf(IsBigIntInstanceType(right_instance_type), &if_right_bigint);
GotoIf(IsJSReceiverInstanceType(right_instance_type),
&if_right_receiver);
......@@ -9253,6 +9268,14 @@ Node* CodeStubAssembler::RelationalComparison(Operation op, Node* left,
var_right.Bind(CallBuiltin(Builtins::kToNumeric, context, right));
Goto(&loop);
BIND(&if_right_bigint);
{
var_result = CAST(CallRuntime(
Runtime::kBigIntCompareToString, NoContextConstant(),
SmiConstant(Reverse(op)), right, left));
Goto(&end);
}
BIND(&if_right_receiver);
{
Callable callable = CodeFactory::NonPrimitiveToPrimitive(
......
......@@ -554,6 +554,14 @@ Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y) {
return Just(
String::Compare(Handle<String>::cast(x), Handle<String>::cast(y)));
}
if (x->IsBigInt() && y->IsString()) {
return Just(BigInt::CompareToString(Handle<BigInt>::cast(x),
Handle<String>::cast(y)));
}
if (x->IsString() && y->IsBigInt()) {
return Just(Reverse(BigInt::CompareToString(Handle<BigInt>::cast(y),
Handle<String>::cast(x))));
}
// ES6 section 7.2.11 Abstract Relational Comparison step 6.
if (!Object::ToNumeric(x).ToHandle(&x) ||
!Object::ToNumeric(y).ToHandle(&y)) {
......
......@@ -659,6 +659,20 @@ MaybeHandle<BigInt> BigInt::Decrement(Handle<BigInt> x) {
return MutableBigInt::MakeImmutable(result);
}
ComparisonResult BigInt::CompareToString(Handle<BigInt> x, Handle<String> y) {
Isolate* isolate = x->GetIsolate();
// a. Let ny be StringToBigInt(y);
MaybeHandle<BigInt> maybe_ny = StringToBigInt(isolate, y);
// b. If ny is NaN, return undefined.
Handle<BigInt> ny;
if (!maybe_ny.ToHandle(&ny)) {
DCHECK(!isolate->has_pending_exception());
return ComparisonResult::kUndefined;
}
// c. Return BigInt::lessThan(x, ny).
return CompareToBigInt(x, ny);
}
bool BigInt::EqualToString(Handle<BigInt> x, Handle<String> y) {
Isolate* isolate = x->GetIsolate();
// a. Let n be StringToBigInt(y).
......
......@@ -134,6 +134,7 @@ class V8_EXPORT_PRIVATE BigInt : public BigIntBase {
static bool EqualToString(Handle<BigInt> x, Handle<String> y);
static bool EqualToNumber(Handle<BigInt> x, Handle<Object> y);
static ComparisonResult CompareToString(Handle<BigInt> x, Handle<String> y);
static ComparisonResult CompareToNumber(Handle<BigInt> x, Handle<Object> y);
// Exposed for tests, do not call directly. Use CompareToNumber() instead.
static ComparisonResult CompareToDouble(Handle<BigInt> x, double y);
......
......@@ -34,6 +34,17 @@ RUNTIME_FUNCTION(Runtime_BigIntCompareToNumber) {
return *isolate->factory()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_BigIntCompareToString) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(Smi, mode, 0);
CONVERT_ARG_HANDLE_CHECKED(BigInt, lhs, 1);
CONVERT_ARG_HANDLE_CHECKED(String, rhs, 2);
bool result = ComparisonResultToBool(static_cast<Operation>(mode->value()),
BigInt::CompareToString(lhs, rhs));
return *isolate->factory()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_BigIntEqualToBigInt) {
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
......
......@@ -68,6 +68,7 @@ namespace internal {
F(BigIntBinaryOp, 3, 1) \
F(BigIntCompareToBigInt, 3, 1) \
F(BigIntCompareToNumber, 3, 1) \
F(BigIntCompareToString, 3, 1) \
F(BigIntEqualToBigInt, 2, 1) \
F(BigIntEqualToNumber, 2, 1) \
F(BigIntEqualToString, 2, 1) \
......
......@@ -429,8 +429,8 @@ const six = BigInt(6);
assertFalse(%Equal("-0x1", minus_one));
const unsafe = "9007199254740993"; // 2**53 + 1
assertTrue(%GreaterThan(eval(unsafe + "n"), unsafe));
assertTrue(%LessThan(unsafe, eval(unsafe + "n")));
assertFalse(%GreaterThan(eval(unsafe + "n"), unsafe));
assertFalse(%LessThan(unsafe, eval(unsafe + "n")));
assertThrows(() => %LessThan(six, Symbol(6)), TypeError);
assertThrows(() => %LessThan(Symbol(6), six), TypeError);
......@@ -509,8 +509,8 @@ const six = BigInt(6);
const unsafe = "9007199254740993"; // 2**53 + 1
assertFalse(eval(unsafe + "n") < unsafe);
assertFalse(eval(unsafe + "n") <= unsafe);
assertTrue(unsafe < eval(unsafe + "n"));
assertTrue(eval(unsafe + "n") <= unsafe);
assertFalse(unsafe < eval(unsafe + "n"));
assertTrue(unsafe <= eval(unsafe + "n"));
assertThrows(() => six < Symbol(6), TypeError);
......
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