Commit e5dafc06 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[bigint] Adapt abstract equality.

This adds BigInt support to JavaScript's abstract equality (== and !=),
implemented mainly via CodeStubAssembler::Equal and via Object::Equals.

Bug: v8:6791
Change-Id: I53219f2f71baa760b142cc676f18931731b87226
Reviewed-on: https://chromium-review.googlesource.com/725701
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48730}
parent e39629c7
...@@ -8478,6 +8478,9 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal, ...@@ -8478,6 +8478,9 @@ void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal,
Goto(if_equal); Goto(if_equal);
} }
// TODO(neis): Introduce BigInt CompareOperationFeedback and collect here
// and elsewhere?
BIND(&if_valueisother); BIND(&if_valueisother);
{ {
CombineFeedback(var_type_feedback, CombineFeedback(var_type_feedback,
...@@ -8509,6 +8512,10 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8509,6 +8512,10 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
do_rhsstringtonumber(this, Label::kDeferred), end(this); do_rhsstringtonumber(this, Label::kDeferred), end(this);
VARIABLE(result, MachineRepresentation::kTagged); VARIABLE(result, MachineRepresentation::kTagged);
// We can avoid code duplication by exploiting the fact that abstract equality
// is symmetric.
Label use_symmetry(this);
// Shared entry for floating point comparison. // Shared entry for floating point comparison.
Label do_fcmp(this); Label do_fcmp(this);
VARIABLE(var_fcmp_lhs, MachineRepresentation::kFloat64); VARIABLE(var_fcmp_lhs, MachineRepresentation::kFloat64);
...@@ -8595,14 +8602,16 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8595,14 +8602,16 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
SmiConstant(CompareOperationFeedback::kAny)); SmiConstant(CompareOperationFeedback::kAny));
} }
// Load the instance type of the {rhs}. // Further inspect {rhs}.
Label if_rhsisstring(this, Label::kDeferred), if_rhsisboolean(this),
if_rhsisbigint(this, Label::kDeferred),
if_rhsisreceiver(this, Label::kDeferred);
Node* rhs_instance_type = LoadMapInstanceType(rhs_map); Node* rhs_instance_type = LoadMapInstanceType(rhs_map);
GotoIf(IsStringInstanceType(rhs_instance_type), &if_rhsisstring);
// Check if the {rhs} is a String. GotoIf(IsBooleanMap(rhs_map), &if_rhsisboolean);
Label if_rhsisstring(this, Label::kDeferred), GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_rhsisbigint);
if_rhsisnotstring(this); Branch(IsJSReceiverInstanceType(rhs_instance_type),
Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, &if_rhsisreceiver, &if_notequal);
&if_rhsisnotstring);
BIND(&if_rhsisstring); BIND(&if_rhsisstring);
{ {
...@@ -8612,13 +8621,6 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8612,13 +8621,6 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
Goto(&do_rhsstringtonumber); Goto(&do_rhsstringtonumber);
} }
BIND(&if_rhsisnotstring);
{
// Check if the {rhs} is a Boolean.
Label if_rhsisboolean(this), if_rhsisnotboolean(this);
Branch(IsBooleanMap(rhs_map), &if_rhsisboolean,
&if_rhsisnotboolean);
BIND(&if_rhsisboolean); BIND(&if_rhsisboolean);
{ {
// The {rhs} is a Boolean, load its number value. // The {rhs} is a Boolean, load its number value.
...@@ -8626,14 +8628,12 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8626,14 +8628,12 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
Goto(&loop); Goto(&loop);
} }
BIND(&if_rhsisnotboolean); BIND(&if_rhsisbigint);
{ {
// Check if the {rhs} is a Receiver. result.Bind(CallRuntime(Runtime::kBigIntEqualToNumber,
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); NoContextConstant(), rhs, lhs));
Label if_rhsisreceiver(this, Label::kDeferred), Goto(&end);
if_rhsisnotreceiver(this); }
Branch(IsJSReceiverInstanceType(rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
BIND(&if_rhsisreceiver); BIND(&if_rhsisreceiver);
{ {
...@@ -8643,37 +8643,18 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8643,37 +8643,18 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
var_rhs.Bind(CallStub(callable, context, rhs)); var_rhs.Bind(CallStub(callable, context, rhs));
Goto(&loop); Goto(&loop);
} }
BIND(&if_rhsisnotreceiver);
Goto(&if_notequal);
}
}
} }
} }
} }
BIND(&if_lhsisnotsmi); BIND(&if_lhsisnotsmi);
{ {
// Check if {rhs} is a Smi or a HeapObject. GotoIf(TaggedIsSmi(rhs), &use_symmetry);
Label if_rhsissmi(this), if_rhsisnotsmi(this);
Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi);
BIND(&if_rhsissmi);
{
// The {lhs} is a HeapObject and the {rhs} is a Smi; swapping {lhs}
// and {rhs} is not observable and doesn't matter for the result, so
// we can just swap them and use the Smi handling above (for {lhs}
// being a Smi).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
Goto(&loop);
}
BIND(&if_rhsisnotsmi); // {rhs} is a HeapObject. Further inspect {lhs}.
{ Label if_lhsissymbol(this), if_lhsisheapnumber(this),
Label if_lhsisstring(this), if_lhsisnumber(this), if_lhsisstring(this), if_lhsisbigint(this, Label::kDeferred),
if_lhsissymbol(this), if_lhsisoddball(this), if_lhsisoddball(this), if_lhsisreceiver(this);
if_lhsisreceiver(this);
// Both {lhs} and {rhs} are HeapObjects, load their maps // Both {lhs} and {rhs} are HeapObjects, load their maps
// and their instance types. // and their instance types.
...@@ -8685,19 +8666,21 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8685,19 +8666,21 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
Node* rhs_instance_type = LoadMapInstanceType(rhs_map); Node* rhs_instance_type = LoadMapInstanceType(rhs_map);
// Dispatch based on the instance type of {lhs}. // Dispatch based on the instance type of {lhs}.
size_t const kNumCases = FIRST_NONSTRING_TYPE + 3; size_t const kNumCases = FIRST_NONSTRING_TYPE + 4;
Label* case_labels[kNumCases]; Label* case_labels[kNumCases];
int32_t case_values[kNumCases]; int32_t case_values[kNumCases];
for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) {
case_labels[i] = new Label(this); case_labels[i] = new Label(this);
case_values[i] = i; case_values[i] = i;
} }
case_labels[FIRST_NONSTRING_TYPE + 0] = &if_lhsisnumber; case_values[FIRST_NONSTRING_TYPE + 0] = SYMBOL_TYPE;
case_values[FIRST_NONSTRING_TYPE + 0] = HEAP_NUMBER_TYPE; case_labels[FIRST_NONSTRING_TYPE + 0] = &if_lhsissymbol;
case_labels[FIRST_NONSTRING_TYPE + 1] = &if_lhsissymbol; case_values[FIRST_NONSTRING_TYPE + 1] = HEAP_NUMBER_TYPE;
case_values[FIRST_NONSTRING_TYPE + 1] = SYMBOL_TYPE; case_labels[FIRST_NONSTRING_TYPE + 1] = &if_lhsisheapnumber;
case_labels[FIRST_NONSTRING_TYPE + 2] = &if_lhsisoddball; case_values[FIRST_NONSTRING_TYPE + 2] = BIGINT_TYPE;
case_values[FIRST_NONSTRING_TYPE + 2] = ODDBALL_TYPE; case_labels[FIRST_NONSTRING_TYPE + 2] = &if_lhsisbigint;
case_values[FIRST_NONSTRING_TYPE + 3] = ODDBALL_TYPE;
case_labels[FIRST_NONSTRING_TYPE + 3] = &if_lhsisoddball;
Switch(lhs_instance_type, &if_lhsisreceiver, case_values, case_labels, Switch(lhs_instance_type, &if_lhsisreceiver, case_values, case_labels,
arraysize(case_values)); arraysize(case_values));
for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) {
...@@ -8709,52 +8692,29 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8709,52 +8692,29 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
BIND(&if_lhsisstring); BIND(&if_lhsisstring);
{ {
// Check if {rhs} is also a String. // Check if {rhs} is also a String.
Label if_rhsisstring(this, Label::kDeferred), Label if_rhsisstring(this, Label::kDeferred);
if_rhsisnotstring(this); GotoIfNot(IsStringInstanceType(rhs_instance_type), &use_symmetry);
Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring,
&if_rhsisnotstring);
BIND(&if_rhsisstring);
{
// Both {lhs} and {rhs} are of type String, just do the // Both {lhs} and {rhs} are of type String, just do the
// string comparison then. // string comparison then.
result.Bind( result.Bind(CallBuiltin(Builtins::kStringEqual, context, lhs, rhs));
CallBuiltin(Builtins::kStringEqual, context, lhs, rhs));
if (var_type_feedback != nullptr) { if (var_type_feedback != nullptr) {
Node* lhs_feedback = Node* lhs_feedback = CollectFeedbackForString(lhs_instance_type);
CollectFeedbackForString(lhs_instance_type); Node* rhs_feedback = CollectFeedbackForString(rhs_instance_type);
Node* rhs_feedback =
CollectFeedbackForString(rhs_instance_type);
CombineFeedback(var_type_feedback, CombineFeedback(var_type_feedback,
SmiOr(lhs_feedback, rhs_feedback)); SmiOr(lhs_feedback, rhs_feedback));
} }
Goto(&end); Goto(&end);
} }
BIND(&if_rhsisnotstring); BIND(&if_lhsisheapnumber);
{
// The {lhs} is a String and the {rhs} is some other HeapObject.
// Swapping {lhs} and {rhs} is not observable and doesn't matter
// for the result, so we can just swap them and use the String
// handling below (for {rhs} being a String).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
if (var_type_feedback != nullptr) {
var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny));
}
Goto(&loop);
}
}
BIND(&if_lhsisnumber);
{ {
// Check if {rhs} is also a HeapNumber. // Check if {rhs} is also a HeapNumber.
Label if_rhsisnumber(this), if_rhsisnotnumber(this); Label if_rhsisheapnumber(this), if_rhsisnotheapnumber(this);
Branch(Word32Equal(lhs_instance_type, rhs_instance_type), Branch(Word32Equal(lhs_instance_type, rhs_instance_type),
&if_rhsisnumber, &if_rhsisnotnumber); &if_rhsisheapnumber, &if_rhsisnotheapnumber);
BIND(&if_rhsisnumber); BIND(&if_rhsisheapnumber);
{ {
// Convert {lhs} and {rhs} to floating point values, and // Convert {lhs} and {rhs} to floating point values, and
// perform a floating point comparison. // perform a floating point comparison.
...@@ -8767,67 +8727,79 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8767,67 +8727,79 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
Goto(&do_fcmp); Goto(&do_fcmp);
} }
BIND(&if_rhsisnotnumber); BIND(&if_rhsisnotheapnumber);
{ {
// The {lhs} is a Number, the {rhs} is some other HeapObject. // The {lhs} is a Number, the {rhs} is some other HeapObject.
Label if_rhsisstring(this, Label::kDeferred), Label if_rhsisstring(this, Label::kDeferred), if_rhsisboolean(this);
if_rhsisnotstring(this);
if (var_type_feedback != nullptr) { if (var_type_feedback != nullptr) {
// The {lhs} is number and {rhs} is not Smi or HeapNumber. // The {lhs} is number and {rhs} is not Smi or HeapNumber.
var_type_feedback->Bind( var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny)); SmiConstant(CompareOperationFeedback::kAny));
} }
Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, GotoIf(IsStringInstanceType(rhs_instance_type), &if_rhsisstring);
&if_rhsisnotstring); GotoIf(IsBooleanMap(rhs_map), &if_rhsisboolean);
GotoIf(IsBigIntInstanceType(rhs_instance_type), &use_symmetry);
Branch(IsJSReceiverInstanceType(rhs_instance_type), &use_symmetry,
&if_notequal);
BIND(&if_rhsisstring); BIND(&if_rhsisstring);
{ {
// The {rhs} is a String and the {lhs} is a HeapNumber; we need // The {lhs} is a HeapNumber and the {rhs} is a String; we need
// to convert the {rhs} to a Number and compare the output to // to convert the {rhs} to a Number.
// the Number on the {lhs}.
Goto(&do_rhsstringtonumber); Goto(&do_rhsstringtonumber);
} }
BIND(&if_rhsisnotstring); BIND(&if_rhsisboolean);
{
// Check if the {rhs} is a JSReceiver.
Label if_rhsisreceiver(this), if_rhsisnotreceiver(this);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Branch(IsJSReceiverInstanceType(rhs_instance_type),
&if_rhsisreceiver, &if_rhsisnotreceiver);
BIND(&if_rhsisreceiver);
{ {
// The {lhs} is a Primitive and the {rhs} is a JSReceiver. // The {rhs} is a Boolean, convert it to a Smi first.
// Swapping {lhs} and {rhs} is not observable and doesn't var_rhs.Bind(LoadObjectField(rhs, Oddball::kToNumberOffset));
// matter for the result, so we can just swap them and use
// the JSReceiver handling below (for {lhs} being a
// JSReceiver).
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
Goto(&loop); Goto(&loop);
} }
}
}
BIND(&if_rhsisnotreceiver); BIND(&if_lhsisbigint);
{ {
// Check if {rhs} is a Boolean. if (var_type_feedback != nullptr) {
Label if_rhsisboolean(this), if_rhsisnotboolean(this); var_type_feedback->Bind(
Branch(IsBooleanMap(rhs_map), &if_rhsisboolean, SmiConstant(CompareOperationFeedback::kAny));
&if_rhsisnotboolean); }
BIND(&if_rhsisboolean); Label if_rhsisheapnumber(this), if_rhsisbigint(this),
if_rhsisstring(this), if_rhsisboolean(this);
GotoIf(IsHeapNumberMap(rhs_map), &if_rhsisheapnumber);
GotoIf(IsBigIntInstanceType(rhs_instance_type), &if_rhsisbigint);
GotoIf(IsStringInstanceType(rhs_instance_type), &if_rhsisstring);
GotoIf(IsBooleanMap(rhs_map), &if_rhsisboolean);
Branch(IsJSReceiverInstanceType(rhs_instance_type), &use_symmetry,
&if_notequal);
BIND(&if_rhsisheapnumber);
{ {
// The {rhs} is a Boolean, convert it to a Smi first. result.Bind(CallRuntime(Runtime::kBigIntEqualToNumber,
var_rhs.Bind( NoContextConstant(), lhs, rhs));
LoadObjectField(rhs, Oddball::kToNumberOffset)); Goto(&end);
Goto(&loop);
} }
BIND(&if_rhsisnotboolean); BIND(&if_rhsisbigint);
Goto(&if_notequal); {
result.Bind(CallRuntime(Runtime::kBigIntEqual, NoContextConstant(),
lhs, rhs));
Goto(&end);
} }
BIND(&if_rhsisstring);
{
result.Bind(CallRuntime(Runtime::kBigIntEqualToString,
NoContextConstant(), lhs, rhs));
Goto(&end);
} }
BIND(&if_rhsisboolean);
{
var_rhs.Bind(LoadObjectField(rhs, Oddball::kToNumberOffset));
Goto(&loop);
} }
} }
...@@ -8878,8 +8850,8 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8878,8 +8850,8 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
{ {
// Check if the {rhs} is a JSReceiver. // Check if the {rhs} is a JSReceiver.
Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); Label if_rhsisreceiver(this), if_rhsisnotreceiver(this);
Branch(IsJSReceiverInstanceType(rhs_instance_type), Branch(IsJSReceiverInstanceType(rhs_instance_type), &if_rhsisreceiver,
&if_rhsisreceiver, &if_rhsisnotreceiver); &if_rhsisnotreceiver);
BIND(&if_rhsisreceiver); BIND(&if_rhsisreceiver);
{ {
...@@ -8891,9 +8863,7 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8891,9 +8863,7 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
var_type_feedback->Bind( var_type_feedback->Bind(
SmiConstant(CompareOperationFeedback::kAny)); SmiConstant(CompareOperationFeedback::kAny));
} }
var_lhs.Bind(rhs); Goto(&use_symmetry);
var_rhs.Bind(lhs);
Goto(&loop);
} }
BIND(&if_rhsisnotreceiver); BIND(&if_rhsisnotreceiver);
...@@ -8907,8 +8877,7 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8907,8 +8877,7 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
BIND(&if_rhsissymbol); BIND(&if_rhsissymbol);
{ {
CombineFeedback( CombineFeedback(var_type_feedback,
var_type_feedback,
SmiConstant(CompareOperationFeedback::kSymbol)); SmiConstant(CompareOperationFeedback::kSymbol));
Goto(&if_notequal); Goto(&if_notequal);
} }
...@@ -8931,15 +8900,14 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8931,15 +8900,14 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
// Check if the {rhs} is also a JSReceiver. // Check if the {rhs} is also a JSReceiver.
Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); Label if_rhsisreceiver(this), if_rhsisnotreceiver(this);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
Branch(IsJSReceiverInstanceType(rhs_instance_type), Branch(IsJSReceiverInstanceType(rhs_instance_type), &if_rhsisreceiver,
&if_rhsisreceiver, &if_rhsisnotreceiver); &if_rhsisnotreceiver);
BIND(&if_rhsisreceiver); BIND(&if_rhsisreceiver);
{ {
if (var_type_feedback != nullptr) { if (var_type_feedback != nullptr) {
// The {lhs} and {rhs} are receivers. // The {lhs} and {rhs} are receivers.
CombineFeedback( CombineFeedback(var_type_feedback,
var_type_feedback,
SmiConstant(CompareOperationFeedback::kReceiver)); SmiConstant(CompareOperationFeedback::kReceiver));
} }
...@@ -8979,7 +8947,6 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8979,7 +8947,6 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
} }
} }
} }
}
BIND(&do_rhsstringtonumber); BIND(&do_rhsstringtonumber);
{ {
...@@ -8988,6 +8955,13 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, ...@@ -8988,6 +8955,13 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context,
} }
} }
BIND(&use_symmetry);
{
var_lhs.Bind(rhs);
var_rhs.Bind(lhs);
Goto(&loop);
}
BIND(&do_fcmp); BIND(&do_fcmp);
{ {
// Load the {lhs} and {rhs} floating point values. // Load the {lhs} and {rhs} floating point values.
......
...@@ -105,7 +105,8 @@ double StringToDouble(UnicodeCache* unicode_cache, ...@@ -105,7 +105,8 @@ double StringToDouble(UnicodeCache* unicode_cache,
double StringToInt(Isolate* isolate, Handle<String> string, int radix); double StringToInt(Isolate* isolate, Handle<String> string, int radix);
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string, V8_EXPORT_PRIVATE MaybeHandle<BigInt> StringToBigInt(Isolate* isolate,
Handle<String> string,
int radix); int radix);
// This version expects a zero-terminated character array. Radix will // This version expects a zero-terminated character array. Radix will
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "src/map-updater.h" #include "src/map-updater.h"
#include "src/messages.h" #include "src/messages.h"
#include "src/objects-body-descriptors-inl.h" #include "src/objects-body-descriptors-inl.h"
#include "src/objects/bigint.h"
#include "src/objects/code-inl.h" #include "src/objects/code-inl.h"
#include "src/objects/compilation-cache-inl.h" #include "src/objects/compilation-cache-inl.h"
#include "src/objects/debug-objects-inl.h" #include "src/objects/debug-objects-inl.h"
...@@ -534,10 +535,8 @@ Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y) { ...@@ -534,10 +535,8 @@ Maybe<ComparisonResult> Object::Compare(Handle<Object> x, Handle<Object> y) {
// static // static
Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
// This is the generic version of Abstract Equality Comparison; a version in // This is the generic version of Abstract Equality Comparison. Must be in
// JavaScript land is available in the EqualStub and NotEqualStub. Whenever // sync with CodeStubAssembler::Equal.
// you change something functionality wise in here, remember to update the
// TurboFan code stubs as well.
while (true) { while (true) {
if (x->IsNumber()) { if (x->IsNumber()) {
if (y->IsNumber()) { if (y->IsNumber()) {
...@@ -546,6 +545,8 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { ...@@ -546,6 +545,8 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (y->IsString()) { } else if (y->IsString()) {
return Just(NumberEquals(x, String::ToNumber(Handle<String>::cast(y)))); return Just(NumberEquals(x, String::ToNumber(Handle<String>::cast(y))));
} else if (y->IsBigInt()) {
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
} else if (y->IsJSReceiver()) { } else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) { .ToHandle(&y)) {
...@@ -564,6 +565,9 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { ...@@ -564,6 +565,9 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
} else if (y->IsBoolean()) { } else if (y->IsBoolean()) {
x = String::ToNumber(Handle<String>::cast(x)); x = String::ToNumber(Handle<String>::cast(x));
return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number())); return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (y->IsBigInt()) {
return Just(BigInt::EqualToString(Handle<BigInt>::cast(y),
Handle<String>::cast(x)));
} else if (y->IsJSReceiver()) { } else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) { .ToHandle(&y)) {
...@@ -580,6 +584,9 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { ...@@ -580,6 +584,9 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
} else if (y->IsString()) { } else if (y->IsString()) {
y = String::ToNumber(Handle<String>::cast(y)); y = String::ToNumber(Handle<String>::cast(y));
return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y)); return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (y->IsBigInt()) {
x = Oddball::ToNumber(Handle<Oddball>::cast(x));
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
} else if (y->IsJSReceiver()) { } else if (y->IsJSReceiver()) {
if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y)) if (!JSReceiver::ToPrimitive(Handle<JSReceiver>::cast(y))
.ToHandle(&y)) { .ToHandle(&y)) {
...@@ -600,6 +607,11 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) { ...@@ -600,6 +607,11 @@ Maybe<bool> Object::Equals(Handle<Object> x, Handle<Object> y) {
} else { } else {
return Just(false); return Just(false);
} }
} else if (x->IsBigInt()) {
if (y->IsBigInt()) {
return Just(BigInt::EqualToBigInt(BigInt::cast(*x), BigInt::cast(*y)));
}
return Equals(y, x);
} else if (x->IsJSReceiver()) { } else if (x->IsJSReceiver()) {
if (y->IsJSReceiver()) { if (y->IsJSReceiver()) {
return Just(x.is_identical_to(y)); return Just(x.is_identical_to(y));
...@@ -10617,7 +10629,6 @@ int ParseDecimalInteger(const uint8_t* s, int from, int to) { ...@@ -10617,7 +10629,6 @@ int ParseDecimalInteger(const uint8_t* s, int from, int to) {
} // namespace } // namespace
// static // static
Handle<Object> String::ToNumber(Handle<String> subject) { Handle<Object> String::ToNumber(Handle<String> subject) {
Isolate* const isolate = subject->GetIsolate(); Isolate* const isolate = subject->GetIsolate();
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class BigInt;
enum AllowNullsFlag { ALLOW_NULLS, DISALLOW_NULLS }; enum AllowNullsFlag { ALLOW_NULLS, DISALLOW_NULLS };
enum RobustnessFlag { ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL }; enum RobustnessFlag { ROBUST_STRING_TRAVERSAL, FAST_STRING_TRAVERSAL };
......
...@@ -21,6 +21,25 @@ RUNTIME_FUNCTION(Runtime_BigIntEqual) { ...@@ -21,6 +21,25 @@ RUNTIME_FUNCTION(Runtime_BigIntEqual) {
bool result = lhs->IsBigInt() && rhs->IsBigInt() && bool result = lhs->IsBigInt() && rhs->IsBigInt() &&
BigInt::EqualToBigInt(BigInt::cast(*lhs), BigInt::cast(*rhs)); BigInt::EqualToBigInt(BigInt::cast(*lhs), BigInt::cast(*rhs));
return *isolate->factory()->ToBoolean(result); return *isolate->factory()->ToBoolean(result);
// TODO(neis): Remove IsBigInt checks?
}
RUNTIME_FUNCTION(Runtime_BigIntEqualToNumber) {
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(BigInt, lhs, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, rhs, 1);
bool result = BigInt::EqualToNumber(lhs, rhs);
return *isolate->factory()->ToBoolean(result);
}
RUNTIME_FUNCTION(Runtime_BigIntEqualToString) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(BigInt, lhs, 0);
CONVERT_ARG_HANDLE_CHECKED(String, rhs, 1);
bool result = BigInt::EqualToString(lhs, rhs);
return *isolate->factory()->ToBoolean(result);
} }
RUNTIME_FUNCTION(Runtime_BigIntToBoolean) { RUNTIME_FUNCTION(Runtime_BigIntToBoolean) {
......
...@@ -71,6 +71,8 @@ namespace internal { ...@@ -71,6 +71,8 @@ namespace internal {
#define FOR_EACH_INTRINSIC_BIGINT(F) \ #define FOR_EACH_INTRINSIC_BIGINT(F) \
F(BigIntBinaryOp, 3, 1) \ F(BigIntBinaryOp, 3, 1) \
F(BigIntEqual, 2, 1) \ F(BigIntEqual, 2, 1) \
F(BigIntEqualToNumber, 2, 1) \
F(BigIntEqualToString, 2, 1) \
F(BigIntToBoolean, 1, 1) \ F(BigIntToBoolean, 1, 1) \
F(BigIntUnaryOp, 2, 1) F(BigIntUnaryOp, 2, 1)
......
...@@ -249,128 +249,6 @@ const six = BigInt(6); ...@@ -249,128 +249,6 @@ const six = BigInt(6);
assertFalse(%CreateIterResultObject(42, zero).done); assertFalse(%CreateIterResultObject(42, zero).done);
} }
// Strict equality
{
assertTrue(zero === zero);
assertFalse(zero !== zero);
assertTrue(zero === another_zero);
assertFalse(zero !== another_zero);
assertFalse(zero === one);
assertTrue(zero !== one);
assertTrue(one !== zero);
assertFalse(one === zero);
assertFalse(zero === 0);
assertTrue(zero !== 0);
assertFalse(0 === zero);
assertTrue(0 !== zero);
}{
assertTrue(%StrictEqual(zero, zero));
assertFalse(%StrictNotEqual(zero, zero));
assertTrue(%StrictEqual(zero, another_zero));
assertFalse(%StrictNotEqual(zero, another_zero));
assertFalse(%StrictEqual(zero, one));
assertTrue(%StrictNotEqual(zero, one));
assertTrue(%StrictNotEqual(one, zero));
assertFalse(%StrictEqual(one, zero));
assertFalse(%StrictEqual(zero, 0));
assertTrue(%StrictNotEqual(zero, 0));
assertFalse(%StrictEqual(0, zero));
assertTrue(%StrictNotEqual(0, zero));
}
// SameValue
{
assertTrue(Object.is(zero, zero));
assertTrue(Object.is(zero, another_zero));
assertTrue(Object.is(one, one));
assertTrue(Object.is(one, another_one));
assertFalse(Object.is(zero, +0));
assertFalse(Object.is(zero, -0));
assertFalse(Object.is(+0, zero));
assertFalse(Object.is(-0, zero));
assertFalse(Object.is(zero, one));
assertFalse(Object.is(one, minus_one));
}{
const obj = Object.defineProperty({}, 'foo',
{value: zero, writable: false, configurable: false});
assertTrue(Reflect.defineProperty(obj, 'foo', {value: zero}));
assertTrue(Reflect.defineProperty(obj, 'foo', {value: another_zero}));
assertFalse(Reflect.defineProperty(obj, 'foo', {value: one}));
}{
assertTrue(%SameValue(zero, zero));
assertTrue(%SameValue(zero, another_zero));
assertFalse(%SameValue(zero, +0));
assertFalse(%SameValue(zero, -0));
assertFalse(%SameValue(+0, zero));
assertFalse(%SameValue(-0, zero));
assertTrue(%SameValue(one, one));
assertTrue(%SameValue(one, another_one));
}
// SameValueZero
{
assertTrue([zero].includes(zero));
assertTrue([zero].includes(another_zero));
assertFalse([zero].includes(+0));
assertFalse([zero].includes(-0));
assertFalse([+0].includes(zero));
assertFalse([-0].includes(zero));
assertTrue([one].includes(one));
assertTrue([one].includes(another_one));
assertFalse([one].includes(1));
assertFalse([1].includes(one));
}{
assertTrue(new Set([zero]).has(zero));
assertTrue(new Set([zero]).has(another_zero));
assertFalse(new Set([zero]).has(+0));
assertFalse(new Set([zero]).has(-0));
assertFalse(new Set([+0]).has(zero));
assertFalse(new Set([-0]).has(zero));
assertTrue(new Set([one]).has(one));
assertTrue(new Set([one]).has(another_one));
}{
assertTrue(new Map([[zero, 42]]).has(zero));
assertTrue(new Map([[zero, 42]]).has(another_zero));
assertFalse(new Map([[zero, 42]]).has(+0));
assertFalse(new Map([[zero, 42]]).has(-0));
assertFalse(new Map([[+0, 42]]).has(zero));
assertFalse(new Map([[-0, 42]]).has(zero));
assertTrue(new Map([[one, 42]]).has(one));
assertTrue(new Map([[one, 42]]).has(another_one));
}{
assertTrue(%SameValueZero(zero, zero));
assertTrue(%SameValueZero(zero, another_zero));
assertFalse(%SameValueZero(zero, +0));
assertFalse(%SameValueZero(zero, -0));
assertFalse(%SameValueZero(+0, zero));
assertFalse(%SameValueZero(-0, zero));
assertTrue(%SameValueZero(one, one));
assertTrue(%SameValueZero(one, another_one));
}
// ToNumber // ToNumber
{ {
assertThrows(() => isNaN(zero), TypeError); assertThrows(() => isNaN(zero), TypeError);
......
// 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: --allow-natives-syntax --harmony-bigint --no-opt
'use strict'
const minus_one = BigInt(-1);
const zero = BigInt(0);
const another_zero = BigInt(0);
const one = BigInt(1);
const another_one = BigInt(1);
const two = BigInt(2);
const three = BigInt(3);
const six = BigInt(6);
// Strict equality
{
assertTrue(zero === zero);
assertFalse(zero !== zero);
assertTrue(zero === another_zero);
assertFalse(zero !== another_zero);
assertFalse(zero === one);
assertTrue(zero !== one);
assertTrue(one !== zero);
assertFalse(one === zero);
assertFalse(zero === 0);
assertTrue(zero !== 0);
assertFalse(0 === zero);
assertTrue(0 !== zero);
}{
assertTrue(%StrictEqual(zero, zero));
assertFalse(%StrictNotEqual(zero, zero));
assertTrue(%StrictEqual(zero, another_zero));
assertFalse(%StrictNotEqual(zero, another_zero));
assertFalse(%StrictEqual(zero, one));
assertTrue(%StrictNotEqual(zero, one));
assertTrue(%StrictNotEqual(one, zero));
assertFalse(%StrictEqual(one, zero));
assertFalse(%StrictEqual(zero, 0));
assertTrue(%StrictNotEqual(zero, 0));
assertFalse(%StrictEqual(0, zero));
assertTrue(%StrictNotEqual(0, zero));
}
// Abstract equality
{
assertTrue(%Equal(zero, zero));
assertTrue(%Equal(zero, another_zero));
assertFalse(%Equal(zero, one));
assertFalse(%Equal(one, zero));
assertTrue(%Equal(zero, +0));
assertTrue(%Equal(zero, -0));
assertTrue(%Equal(+0, zero));
assertTrue(%Equal(-0, zero));
assertTrue(%Equal(zero, false));
assertTrue(%Equal(one, true));
assertFalse(%Equal(zero, true));
assertFalse(%Equal(one, false));
assertTrue(%Equal(false, zero));
assertTrue(%Equal(true, one));
assertFalse(%Equal(true, zero));
assertFalse(%Equal(false, one));
assertTrue(%Equal(one, 1));
assertTrue(%Equal(one, Number(1)));
assertTrue(%Equal(1, one));
assertTrue(%Equal(Number(1), one));
assertTrue(%Equal(minus_one, -1));
assertTrue(%Equal(minus_one, Number(-1)));
assertTrue(%Equal(-1, minus_one));
assertTrue(%Equal(Number(-1), minus_one));
assertFalse(%Equal(one, -1));
assertFalse(%Equal(one, Number(-1)));
assertFalse(%Equal(-1, one));
assertFalse(%Equal(Number(-1), one));
assertFalse(%Equal(one, 1.0000000000001));
assertFalse(%Equal(1.0000000000001, one));
// TODO(neis): Empty string must get converted to 0n.
//assertTrue(%Equal(zero, ""));
//assertTrue(%Equal("", zero));
assertTrue(%Equal(one, "1"));
assertTrue(%Equal("1", one));
assertTrue(%Equal(one, {valueOf() { return true }}));
assertTrue(%Equal({valueOf() { return true }}, one));
assertFalse(%Equal(two, {valueOf() { return true }}));
assertFalse(%Equal({valueOf() { return true }}, two));
assertFalse(%Equal(Symbol(), zero));
assertFalse(%Equal(zero, Symbol()));
}{
assertTrue(zero == zero);
assertTrue(zero == another_zero);
assertFalse(zero == one);
assertFalse(one == zero);
assertTrue(zero == +0);
assertTrue(zero == -0);
assertTrue(+0 == zero);
assertTrue(-0 == zero);
assertTrue(zero == false);
assertTrue(one == true);
assertFalse(zero == true);
assertFalse(one == false);
assertTrue(false == zero);
assertTrue(true == one);
assertFalse(true == zero);
assertFalse(false == one);
assertTrue(one == 1);
assertTrue(one == Number(1));
assertTrue(1 == one);
assertTrue(Number(1) == one);
assertTrue(minus_one == -1);
assertTrue(minus_one == Number(-1));
assertTrue(-1 == minus_one);
assertTrue(Number(-1) == minus_one);
assertFalse(one == -1);
assertFalse(one == Number(-1));
assertFalse(-1 == one);
assertFalse(Number(-1) == one);
assertFalse(one == 1.0000000000001);
assertFalse(1.0000000000001 == one);
//assertTrue(zero == "");
//assertTrue("" == zero);
assertTrue(one == "1");
assertTrue("1" == one);
assertTrue(one == {valueOf() { return true }});
assertTrue({valueOf() { return true }} == one);
assertFalse(two == {valueOf() { return true }});
assertFalse({valueOf() { return true }} == two);
assertFalse(Symbol() == zero);
assertFalse(zero == Symbol());
}{
assertFalse(%NotEqual(zero, zero));
assertFalse(%NotEqual(zero, another_zero));
assertTrue(%NotEqual(zero, one));
assertTrue(%NotEqual(one, zero));
assertFalse(%NotEqual(zero, +0));
assertFalse(%NotEqual(zero, -0));
assertFalse(%NotEqual(+0, zero));
assertFalse(%NotEqual(-0, zero));
assertFalse(%NotEqual(zero, false));
assertFalse(%NotEqual(one, true));
assertTrue(%NotEqual(zero, true));
assertTrue(%NotEqual(one, false));
assertFalse(%NotEqual(false, zero));
assertFalse(%NotEqual(true, one));
assertTrue(%NotEqual(true, zero));
assertTrue(%NotEqual(false, one));
assertFalse(%NotEqual(one, 1));
assertFalse(%NotEqual(one, Number(1)));
assertFalse(%NotEqual(1, one));
assertFalse(%NotEqual(Number(1), one));
assertFalse(%NotEqual(minus_one, -1));
assertFalse(%NotEqual(minus_one, Number(-1)));
assertFalse(%NotEqual(-1, minus_one));
assertFalse(%NotEqual(Number(-1), minus_one));
assertTrue(%NotEqual(one, -1));
assertTrue(%NotEqual(one, Number(-1)));
assertTrue(%NotEqual(-1, one));
assertTrue(%NotEqual(Number(-1), one));
assertTrue(%NotEqual(one, 1.0000000000001));
assertTrue(%NotEqual(1.0000000000001, one));
//assertFalse(%NotEqual(zero, ""));
//assertFalse(%NotEqual("", zero));
assertFalse(%NotEqual(one, "1"));
assertFalse(%NotEqual("1", one));
assertFalse(%NotEqual(one, {valueOf() { return true }}));
assertFalse(%NotEqual({valueOf() { return true }}, one));
assertTrue(%NotEqual(two, {valueOf() { return true }}));
assertTrue(%NotEqual({valueOf() { return true }}, two));
assertTrue(%NotEqual(Symbol(), zero));
assertTrue(%NotEqual(zero, Symbol()));
}{
assertFalse(zero != zero);
assertFalse(zero != another_zero);
assertTrue(zero != one);
assertTrue(one != zero);
assertFalse(zero != +0);
assertFalse(zero != -0);
assertFalse(+0 != zero);
assertFalse(-0 != zero);
assertFalse(zero != false);
assertFalse(one != true);
assertTrue(zero != true);
assertTrue(one != false);
assertFalse(false != zero);
assertFalse(true != one);
assertTrue(true != zero);
assertTrue(false != one);
assertFalse(one != 1);
assertFalse(one != Number(1));
assertFalse(1 != one);
assertFalse(Number(1) != one);
assertFalse(minus_one != -1);
assertFalse(minus_one != Number(-1));
assertFalse(-1 != minus_one);
assertFalse(Number(-1) != minus_one);
assertTrue(one != -1);
assertTrue(one != Number(-1));
assertTrue(-1 != one);
assertTrue(Number(-1) != one);
assertTrue(one != 1.0000000000001);
assertTrue(1.0000000000001 != one);
//assertFalse(zero != "");
//assertFalse("" != zero);
assertFalse(one != "1");
assertFalse("1" != one);
assertFalse(one != {valueOf() { return true }});
assertFalse({valueOf() { return true }} != one);
assertTrue(two != {valueOf() { return true }});
assertTrue({valueOf() { return true }} != two);
assertTrue(Symbol() != zero);
assertTrue(zero != Symbol());
}
// SameValue
{
assertTrue(Object.is(zero, zero));
assertTrue(Object.is(zero, another_zero));
assertTrue(Object.is(one, one));
assertTrue(Object.is(one, another_one));
assertFalse(Object.is(zero, +0));
assertFalse(Object.is(zero, -0));
assertFalse(Object.is(+0, zero));
assertFalse(Object.is(-0, zero));
assertFalse(Object.is(zero, one));
assertFalse(Object.is(one, minus_one));
}{
const obj = Object.defineProperty({}, 'foo',
{value: zero, writable: false, configurable: false});
assertTrue(Reflect.defineProperty(obj, 'foo', {value: zero}));
assertTrue(Reflect.defineProperty(obj, 'foo', {value: another_zero}));
assertFalse(Reflect.defineProperty(obj, 'foo', {value: one}));
}{
assertTrue(%SameValue(zero, zero));
assertTrue(%SameValue(zero, another_zero));
assertFalse(%SameValue(zero, +0));
assertFalse(%SameValue(zero, -0));
assertFalse(%SameValue(+0, zero));
assertFalse(%SameValue(-0, zero));
assertTrue(%SameValue(one, one));
assertTrue(%SameValue(one, another_one));
}
// SameValueZero
{
assertTrue([zero].includes(zero));
assertTrue([zero].includes(another_zero));
assertFalse([zero].includes(+0));
assertFalse([zero].includes(-0));
assertFalse([+0].includes(zero));
assertFalse([-0].includes(zero));
assertTrue([one].includes(one));
assertTrue([one].includes(another_one));
assertFalse([one].includes(1));
assertFalse([1].includes(one));
}{
assertTrue(new Set([zero]).has(zero));
assertTrue(new Set([zero]).has(another_zero));
assertFalse(new Set([zero]).has(+0));
assertFalse(new Set([zero]).has(-0));
assertFalse(new Set([+0]).has(zero));
assertFalse(new Set([-0]).has(zero));
assertTrue(new Set([one]).has(one));
assertTrue(new Set([one]).has(another_one));
}{
assertTrue(new Map([[zero, 42]]).has(zero));
assertTrue(new Map([[zero, 42]]).has(another_zero));
assertFalse(new Map([[zero, 42]]).has(+0));
assertFalse(new Map([[zero, 42]]).has(-0));
assertFalse(new Map([[+0, 42]]).has(zero));
assertFalse(new Map([[-0, 42]]).has(zero));
assertTrue(new Map([[one, 42]]).has(one));
assertTrue(new Map([[one, 42]]).has(another_one));
}{
assertTrue(%SameValueZero(zero, zero));
assertTrue(%SameValueZero(zero, another_zero));
assertFalse(%SameValueZero(zero, +0));
assertFalse(%SameValueZero(zero, -0));
assertFalse(%SameValueZero(+0, zero));
assertFalse(%SameValueZero(-0, zero));
assertTrue(%SameValueZero(one, one));
assertTrue(%SameValueZero(one, another_one));
}
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