summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common.gypi2
-rw-r--r--deps/v8/src/code-stub-assembler.cc97
-rw-r--r--deps/v8/src/code-stub-assembler.h9
-rw-r--r--deps/v8/src/ic/accessor-assembler.cc46
-rw-r--r--deps/v8/src/lookup.cc8
-rw-r--r--deps/v8/src/objects-inl.h10
-rw-r--r--deps/v8/src/objects.cc48
-rw-r--r--deps/v8/src/objects.h4
-rw-r--r--deps/v8/test/cctest/test-field-type-tracking.cc186
-rw-r--r--deps/v8/test/mjsunit/regress/regress-crbug-950747.js11
10 files changed, 328 insertions, 93 deletions
diff --git a/common.gypi b/common.gypi
index 83efbb8898..223043e551 100644
--- a/common.gypi
+++ b/common.gypi
@@ -38,7 +38,7 @@
# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
- 'v8_embedder_string': '-node.17',
+ 'v8_embedder_string': '-node.18',
##### V8 defaults for Node.js #####
diff --git a/deps/v8/src/code-stub-assembler.cc b/deps/v8/src/code-stub-assembler.cc
index e4dba15750..8f4930e560 100644
--- a/deps/v8/src/code-stub-assembler.cc
+++ b/deps/v8/src/code-stub-assembler.cc
@@ -12367,7 +12367,7 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs,
// This algorithm differs from the Strict Equality Comparison Algorithm in its
// treatment of signed zeroes and NaNs.
void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true,
- Label* if_false) {
+ Label* if_false, SameValueMode mode) {
VARIABLE(var_lhs_value, MachineRepresentation::kFloat64);
VARIABLE(var_rhs_value, MachineRepresentation::kFloat64);
Label do_fcmp(this);
@@ -12413,10 +12413,12 @@ void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true,
if_lhsisbigint(this);
Node* const lhs_map = LoadMap(lhs);
GotoIf(IsHeapNumberMap(lhs_map), &if_lhsisheapnumber);
- Node* const lhs_instance_type = LoadMapInstanceType(lhs_map);
- GotoIf(IsStringInstanceType(lhs_instance_type), &if_lhsisstring);
- Branch(IsBigIntInstanceType(lhs_instance_type), &if_lhsisbigint,
- if_false);
+ if (mode != SameValueMode::kNumbersOnly) {
+ Node* const lhs_instance_type = LoadMapInstanceType(lhs_map);
+ GotoIf(IsStringInstanceType(lhs_instance_type), &if_lhsisstring);
+ GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_lhsisbigint);
+ }
+ Goto(if_false);
BIND(&if_lhsisheapnumber);
{
@@ -12426,53 +12428,62 @@ void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true,
Goto(&do_fcmp);
}
- BIND(&if_lhsisstring);
- {
- // Now we can only yield true if {rhs} is also a String
- // with the same sequence of characters.
- GotoIfNot(IsString(rhs), if_false);
- Node* const result = CallBuiltin(Builtins::kStringEqual,
- NoContextConstant(), lhs, rhs);
- Branch(IsTrue(result), if_true, if_false);
- }
-
- BIND(&if_lhsisbigint);
- {
- GotoIfNot(IsBigInt(rhs), if_false);
- Node* const result = CallRuntime(Runtime::kBigIntEqualToBigInt,
- NoContextConstant(), lhs, rhs);
- Branch(IsTrue(result), if_true, if_false);
+ if (mode != SameValueMode::kNumbersOnly) {
+ BIND(&if_lhsisstring);
+ {
+ // Now we can only yield true if {rhs} is also a String
+ // with the same sequence of characters.
+ GotoIfNot(IsString(rhs), if_false);
+ Node* const result = CallBuiltin(
+ Builtins::kStringEqual, NoContextConstant(), lhs, rhs);
+ Branch(IsTrue(result), if_true, if_false);
+ }
+
+ BIND(&if_lhsisbigint);
+ {
+ GotoIfNot(IsBigInt(rhs), if_false);
+ Node* const result =
+ CallRuntime(Runtime::kBigIntEqualToBigInt,
+ NoContextConstant(), lhs, rhs);
+ Branch(IsTrue(result), if_true, if_false);
+ }
}
});
}
BIND(&do_fcmp);
{
- Node* const lhs_value = var_lhs_value.value();
- Node* const rhs_value = var_rhs_value.value();
+ TNode<Float64T> lhs_value = UncheckedCast<Float64T>(var_lhs_value.value());
+ TNode<Float64T> rhs_value = UncheckedCast<Float64T>(var_rhs_value.value());
+ BranchIfSameNumberValue(lhs_value, rhs_value, if_true, if_false);
+ }
+}
- Label if_equal(this), if_notequal(this);
- Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal);
+void CodeStubAssembler::BranchIfSameNumberValue(TNode<Float64T> lhs_value,
+ TNode<Float64T> rhs_value,
+ Label* if_true,
+ Label* if_false) {
+ Label if_equal(this), if_notequal(this);
+ Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal);
- BIND(&if_equal);
- {
- // We still need to handle the case when {lhs} and {rhs} are -0.0 and
- // 0.0 (or vice versa). Compare the high word to
- // distinguish between the two.
- Node* const lhs_hi_word = Float64ExtractHighWord32(lhs_value);
- Node* const rhs_hi_word = Float64ExtractHighWord32(rhs_value);
-
- // If x is +0 and y is -0, return false.
- // If x is -0 and y is +0, return false.
- Branch(Word32Equal(lhs_hi_word, rhs_hi_word), if_true, if_false);
- }
+ BIND(&if_equal);
+ {
+ // We still need to handle the case when {lhs} and {rhs} are -0.0 and
+ // 0.0 (or vice versa). Compare the high word to
+ // distinguish between the two.
+ Node* const lhs_hi_word = Float64ExtractHighWord32(lhs_value);
+ Node* const rhs_hi_word = Float64ExtractHighWord32(rhs_value);
- BIND(&if_notequal);
- {
- // Return true iff both {rhs} and {lhs} are NaN.
- GotoIf(Float64Equal(lhs_value, lhs_value), if_false);
- Branch(Float64Equal(rhs_value, rhs_value), if_false, if_true);
- }
+ // If x is +0 and y is -0, return false.
+ // If x is -0 and y is +0, return false.
+ Branch(Word32Equal(lhs_hi_word, rhs_hi_word), if_true, if_false);
+ }
+
+ BIND(&if_notequal);
+ {
+ // Return true iff both {rhs} and {lhs} are NaN.
+ GotoIf(Float64Equal(lhs_value, lhs_value), if_false);
+ Branch(Float64Equal(rhs_value, rhs_value), if_false, if_true);
}
}
diff --git a/deps/v8/src/code-stub-assembler.h b/deps/v8/src/code-stub-assembler.h
index 86cc275c14..c90b5d0e7e 100644
--- a/deps/v8/src/code-stub-assembler.h
+++ b/deps/v8/src/code-stub-assembler.h
@@ -3095,7 +3095,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// ECMA#sec-samevalue
// Similar to StrictEqual except that NaNs are treated as equal and minus zero
// differs from positive zero.
- void BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, Label* if_false);
+ enum class SameValueMode { kNumbersOnly, kFull };
+ void BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, Label* if_false,
+ SameValueMode mode = SameValueMode::kFull);
+ // A part of BranchIfSameValue() that handles two double values.
+ // Treats NaN == NaN and +0 != -0.
+ void BranchIfSameNumberValue(TNode<Float64T> lhs_value,
+ TNode<Float64T> rhs_value, Label* if_true,
+ Label* if_false);
enum HasPropertyLookupMode { kHasProperty, kForInHasProperty };
diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc
index bc636e4164..fba761725b 100644
--- a/deps/v8/src/ic/accessor-assembler.cc
+++ b/deps/v8/src/ic/accessor-assembler.cc
@@ -1202,23 +1202,23 @@ void AccessorAssembler::OverwriteExistingFastDataProperty(
BIND(&inobject);
{
- Node* field_offset = TimesTaggedSize(field_index);
+ TNode<IntPtrT> field_offset = Signed(TimesTaggedSize(field_index));
Label tagged_rep(this), double_rep(this);
Branch(
Word32Equal(representation, Int32Constant(Representation::kDouble)),
&double_rep, &tagged_rep);
BIND(&double_rep);
{
- Node* double_value = ChangeNumberToFloat64(value);
+ TNode<Float64T> double_value = ChangeNumberToFloat64(value);
if (FLAG_unbox_double_fields) {
if (do_transitioning_store) {
StoreMap(object, object_map);
} else if (FLAG_track_constant_fields) {
Label if_mutable(this);
GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
- Node* current_value =
- LoadObjectField(object, field_offset, MachineType::Float64());
- Branch(Float64Equal(current_value, double_value), &done, slow);
+ TNode<Float64T> current_value =
+ LoadObjectField<Float64T>(CAST(object), field_offset);
+ BranchIfSameNumberValue(current_value, double_value, &done, slow);
BIND(&if_mutable);
}
StoreObjectFieldNoWriteBarrier(object, field_offset, double_value,
@@ -1234,8 +1234,9 @@ void AccessorAssembler::OverwriteExistingFastDataProperty(
if (FLAG_track_constant_fields) {
Label if_mutable(this);
GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
- Node* current_value = LoadHeapNumberValue(mutable_heap_number);
- Branch(Float64Equal(current_value, double_value), &done, slow);
+ TNode<Float64T> current_value =
+ LoadHeapNumberValue(mutable_heap_number);
+ BranchIfSameNumberValue(current_value, double_value, &done, slow);
BIND(&if_mutable);
}
StoreHeapNumberValue(mutable_heap_number, double_value);
@@ -1251,9 +1252,10 @@ void AccessorAssembler::OverwriteExistingFastDataProperty(
} else if (FLAG_track_constant_fields) {
Label if_mutable(this);
GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
- Node* current_value =
- LoadObjectField(object, field_offset, MachineType::AnyTagged());
- Branch(WordEqual(current_value, value), &done, slow);
+ TNode<Object> current_value =
+ LoadObjectField(CAST(object), field_offset);
+ BranchIfSameValue(current_value, value, &done, slow,
+ SameValueMode::kNumbersOnly);
BIND(&if_mutable);
}
StoreObjectField(object, field_offset, value);
@@ -1303,12 +1305,13 @@ void AccessorAssembler::OverwriteExistingFastDataProperty(
{
Node* mutable_heap_number =
LoadPropertyArrayElement(properties, backing_store_index);
- Node* double_value = ChangeNumberToFloat64(value);
+ TNode<Float64T> double_value = ChangeNumberToFloat64(value);
if (FLAG_track_constant_fields) {
Label if_mutable(this);
GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
- Node* current_value = LoadHeapNumberValue(mutable_heap_number);
- Branch(Float64Equal(current_value, double_value), &done, slow);
+ TNode<Float64T> current_value =
+ LoadHeapNumberValue(mutable_heap_number);
+ BranchIfSameNumberValue(current_value, double_value, &done, slow);
BIND(&if_mutable);
}
StoreHeapNumberValue(mutable_heap_number, double_value);
@@ -1319,9 +1322,10 @@ void AccessorAssembler::OverwriteExistingFastDataProperty(
if (FLAG_track_constant_fields) {
Label if_mutable(this);
GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
- Node* current_value =
+ TNode<Object> current_value =
LoadPropertyArrayElement(properties, backing_store_index);
- Branch(WordEqual(current_value, value), &done, slow);
+ BranchIfSameValue(current_value, value, &done, slow,
+ SameValueMode::kNumbersOnly);
BIND(&if_mutable);
}
StorePropertyArrayElement(properties, backing_store_index, value);
@@ -1813,7 +1817,7 @@ void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object,
}
Node* index = DecodeWord<StoreHandler::FieldIndexBits>(handler_word);
- Node* offset = IntPtrMul(index, IntPtrConstant(kTaggedSize));
+ TNode<IntPtrT> offset = Signed(TimesTaggedSize(index));
if (representation.IsDouble()) {
if (!FLAG_unbox_double_fields || !is_inobject) {
// Load the mutable heap number.
@@ -1831,14 +1835,14 @@ void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object,
&done);
{
if (store_value_as_double) {
- Node* current_value =
- LoadObjectField(property_storage, offset, MachineType::Float64());
- GotoIfNot(Float64Equal(current_value, value), bailout);
+ TNode<Float64T> current_value =
+ LoadObjectField<Float64T>(CAST(property_storage), offset);
+ BranchIfSameNumberValue(current_value, UncheckedCast<Float64T>(value),
+ &done, bailout);
} else {
Node* current_value = LoadObjectField(property_storage, offset);
- GotoIfNot(WordEqual(current_value, value), bailout);
+ Branch(WordEqual(current_value, value), &done, bailout);
}
- Goto(&done);
}
BIND(&done);
}
diff --git a/deps/v8/src/lookup.cc b/deps/v8/src/lookup.cc
index cc5d13dd6b..0e8a202a07 100644
--- a/deps/v8/src/lookup.cc
+++ b/deps/v8/src/lookup.cc
@@ -934,10 +934,14 @@ bool LookupIterator::IsConstFieldValueEqualTo(Object value) const {
// Uninitialized double field.
return true;
}
- return bit_cast<double>(bits) == value->Number();
+ return Object::SameNumberValue(bit_cast<double>(bits), value->Number());
} else {
Object current_value = holder->RawFastPropertyAt(field_index);
- return current_value->IsUninitialized(isolate()) || current_value == value;
+ if (current_value->IsUninitialized(isolate()) || current_value == value) {
+ return true;
+ }
+ return current_value->IsNumber() && value->IsNumber() &&
+ Object::SameNumberValue(current_value->Number(), value->Number());
}
}
diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h
index 6f146241b1..33b2cb9bca 100644
--- a/deps/v8/src/objects-inl.h
+++ b/deps/v8/src/objects-inl.h
@@ -383,6 +383,16 @@ double Object::Number() const {
: HeapNumber::unchecked_cast(*this)->value();
}
+// static
+bool Object::SameNumberValue(double value1, double value2) {
+ // SameNumberValue(NaN, NaN) is true.
+ if (value1 != value2) {
+ return std::isnan(value1) && std::isnan(value2);
+ }
+ // SameNumberValue(0.0, -0.0) is false.
+ return (std::signbit(value1) == std::signbit(value2));
+}
+
bool Object::IsNaN() const {
return this->IsHeapNumber() && std::isnan(HeapNumber::cast(*this)->value());
}
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 8337b3d4cf..98b838e478 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -594,7 +594,7 @@ namespace {
// TODO(bmeurer): Maybe we should introduce a marker interface Number,
// where we put all these methods at some point?
-ComparisonResult NumberCompare(double x, double y) {
+ComparisonResult StrictNumberCompare(double x, double y) {
if (std::isnan(x) || std::isnan(y)) {
return ComparisonResult::kUndefined;
} else if (x < y) {
@@ -606,19 +606,20 @@ ComparisonResult NumberCompare(double x, double y) {
}
}
-bool NumberEquals(double x, double y) {
+// See Number case of ES6#sec-strict-equality-comparison
+// Returns false if x or y is NaN, treats -0.0 as equal to 0.0.
+bool StrictNumberEquals(double x, double y) {
// Must check explicitly for NaN's on Windows, but -0 works fine.
- if (std::isnan(x)) return false;
- if (std::isnan(y)) return false;
+ if (std::isnan(x) || std::isnan(y)) return false;
return x == y;
}
-bool NumberEquals(const Object x, const Object y) {
- return NumberEquals(x->Number(), y->Number());
+bool StrictNumberEquals(const Object x, const Object y) {
+ return StrictNumberEquals(x->Number(), y->Number());
}
-bool NumberEquals(Handle<Object> x, Handle<Object> y) {
- return NumberEquals(*x, *y);
+bool StrictNumberEquals(Handle<Object> x, Handle<Object> y) {
+ return StrictNumberEquals(*x, *y);
}
ComparisonResult Reverse(ComparisonResult result) {
@@ -663,7 +664,7 @@ Maybe<ComparisonResult> Object::Compare(Isolate* isolate, Handle<Object> x,
bool x_is_number = x->IsNumber();
bool y_is_number = y->IsNumber();
if (x_is_number && y_is_number) {
- return Just(NumberCompare(x->Number(), y->Number()));
+ return Just(StrictNumberCompare(x->Number(), y->Number()));
} else if (!x_is_number && !y_is_number) {
return Just(BigInt::CompareToBigInt(Handle<BigInt>::cast(x),
Handle<BigInt>::cast(y)));
@@ -683,11 +684,12 @@ Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
while (true) {
if (x->IsNumber()) {
if (y->IsNumber()) {
- return Just(NumberEquals(x, y));
+ return Just(StrictNumberEquals(x, y));
} else if (y->IsBoolean()) {
- return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
+ return Just(
+ StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (y->IsString()) {
- return Just(NumberEquals(
+ return Just(StrictNumberEquals(
x, String::ToNumber(isolate, Handle<String>::cast(y))));
} else if (y->IsBigInt()) {
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
@@ -705,10 +707,11 @@ Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
Handle<String>::cast(y)));
} else if (y->IsNumber()) {
x = String::ToNumber(isolate, Handle<String>::cast(x));
- return Just(NumberEquals(x, y));
+ return Just(StrictNumberEquals(x, y));
} else if (y->IsBoolean()) {
x = String::ToNumber(isolate, Handle<String>::cast(x));
- return Just(NumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
+ return Just(
+ StrictNumberEquals(*x, Handle<Oddball>::cast(y)->to_number()));
} else if (y->IsBigInt()) {
return Just(BigInt::EqualToString(isolate, Handle<BigInt>::cast(y),
Handle<String>::cast(x)));
@@ -724,10 +727,12 @@ Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
if (y->IsOddball()) {
return Just(x.is_identical_to(y));
} else if (y->IsNumber()) {
- return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
+ return Just(
+ StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (y->IsString()) {
y = String::ToNumber(isolate, Handle<String>::cast(y));
- return Just(NumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
+ return Just(
+ StrictNumberEquals(Handle<Oddball>::cast(x)->to_number(), *y));
} else if (y->IsBigInt()) {
x = Oddball::ToNumber(isolate, Handle<Oddball>::cast(x));
return Just(BigInt::EqualToNumber(Handle<BigInt>::cast(y), x));
@@ -776,7 +781,7 @@ Maybe<bool> Object::Equals(Isolate* isolate, Handle<Object> x,
bool Object::StrictEquals(Object that) {
if (this->IsNumber()) {
if (!that->IsNumber()) return false;
- return NumberEquals(*this, that);
+ return StrictNumberEquals(*this, that);
} else if (this->IsString()) {
if (!that->IsString()) return false;
return String::cast(*this)->Equals(String::cast(that));
@@ -1624,14 +1629,7 @@ bool Object::SameValue(Object other) {
if (other == *this) return true;
if (IsNumber() && other->IsNumber()) {
- double this_value = Number();
- double other_value = other->Number();
- // SameValue(NaN, NaN) is true.
- if (this_value != other_value) {
- return std::isnan(this_value) && std::isnan(other_value);
- }
- // SameValue(0.0, -0.0) is false.
- return (std::signbit(this_value) == std::signbit(other_value));
+ return SameNumberValue(Number(), other->Number());
}
if (IsString() && other->IsString()) {
return String::cast(*this)->Equals(String::cast(other));
diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h
index d7cb4f39d0..6c0f313261 100644
--- a/deps/v8/src/objects.h
+++ b/deps/v8/src/objects.h
@@ -854,6 +854,10 @@ class Object {
// to implement the Object.is function.
V8_EXPORT_PRIVATE bool SameValue(Object other);
+ // A part of SameValue which handles Number vs. Number case.
+ // Treats NaN == NaN and +0 != -0.
+ inline static bool SameNumberValue(double number1, double number2);
+
// Checks whether this object has the same value as the given one.
// +0 and -0 are treated equal. Everything else is the same as SameValue.
// This function is implemented according to ES6, section 7.2.4 and is used
diff --git a/deps/v8/test/cctest/test-field-type-tracking.cc b/deps/v8/test/cctest/test-field-type-tracking.cc
index 49b0f92011..0253a16735 100644
--- a/deps/v8/test/cctest/test-field-type-tracking.cc
+++ b/deps/v8/test/cctest/test-field-type-tracking.cc
@@ -2883,6 +2883,192 @@ TEST(HoleyMutableHeapNumber) {
CHECK_EQ(kHoleNanInt64, MutableHeapNumber::cast(*obj)->value_as_bits());
}
+namespace {
+
+template <class... Args>
+MaybeHandle<Object> Call(Isolate* isolate, Handle<JSFunction> function,
+ Args... args) {
+ Handle<Object> argv[] = {args...};
+ return Execution::Call(isolate, function,
+ isolate->factory()->undefined_value(), sizeof...(args),
+ argv);
+}
+
+void TestStoreToConstantField(const char* store_func_source,
+ Handle<Object> value1, Handle<Object> value2,
+ Representation expected_rep,
+ PropertyConstness expected_constness,
+ int store_repetitions) {
+ Isolate* isolate = CcTest::i_isolate();
+ CompileRun(store_func_source);
+
+ Handle<JSFunction> store_func = GetGlobal<JSFunction>("store");
+
+ const PropertyConstness kExpectedInitialFieldConstness =
+ FLAG_track_constant_fields ? PropertyConstness::kConst
+ : PropertyConstness::kMutable;
+
+ Handle<Map> initial_map = Map::Create(isolate, 4);
+
+ // Store value1 to obj1 and check that it got property with expected
+ // representation and constness.
+ Handle<JSObject> obj1 = isolate->factory()->NewJSObjectFromMap(initial_map);
+ for (int i = 0; i < store_repetitions; i++) {
+ Call(isolate, store_func, obj1, value1).Check();
+ }
+
+ Handle<Map> map(obj1->map(), isolate);
+ CHECK(!map->is_dictionary_map());
+ CHECK(!map->is_deprecated());
+ CHECK_EQ(1, map->NumberOfOwnDescriptors());
+
+ CHECK(map->instance_descriptors()->GetDetails(0).representation().Equals(
+ expected_rep));
+ CHECK_EQ(kExpectedInitialFieldConstness,
+ map->instance_descriptors()->GetDetails(0).constness());
+
+ // Store value2 to obj2 and check that it got same map and property details
+ // did not change.
+ Handle<JSObject> obj2 = isolate->factory()->NewJSObjectFromMap(initial_map);
+ Call(isolate, store_func, obj2, value2).Check();
+
+ CHECK_EQ(*map, obj2->map());
+ CHECK(!map->is_dictionary_map());
+ CHECK(!map->is_deprecated());
+ CHECK_EQ(1, map->NumberOfOwnDescriptors());
+
+ CHECK(map->instance_descriptors()->GetDetails(0).representation().Equals(
+ expected_rep));
+ CHECK_EQ(kExpectedInitialFieldConstness,
+ map->instance_descriptors()->GetDetails(0).constness());
+
+ // Store value2 to obj1 and check that property became mutable.
+ Call(isolate, store_func, obj1, value2).Check();
+
+ CHECK_EQ(*map, obj1->map());
+ CHECK(!map->is_dictionary_map());
+ CHECK(!map->is_deprecated());
+ CHECK_EQ(1, map->NumberOfOwnDescriptors());
+
+ CHECK(map->instance_descriptors()->GetDetails(0).representation().Equals(
+ expected_rep));
+ CHECK_EQ(expected_constness,
+ map->instance_descriptors()->GetDetails(0).constness());
+}
+
+void TestStoreToConstantField_PlusMinusZero(const char* store_func_source,
+ int store_repetitions) {
+ Isolate* isolate = CcTest::i_isolate();
+ CompileRun(store_func_source);
+
+ Handle<Object> minus_zero = isolate->factory()->NewNumber(-0.0);
+ Handle<Object> plus_zero = isolate->factory()->NewNumber(0.0);
+
+ // +0 and -0 are treated as not equal upon stores.
+ const PropertyConstness kExpectedFieldConstness = PropertyConstness::kMutable;
+
+ TestStoreToConstantField(store_func_source, minus_zero, plus_zero,
+ Representation::Double(), kExpectedFieldConstness,
+ store_repetitions);
+}
+
+void TestStoreToConstantField_NaN(const char* store_func_source,
+ int store_repetitions) {
+ Isolate* isolate = CcTest::i_isolate();
+ CompileRun(store_func_source);
+
+ uint64_t nan_bits = uint64_t{0x7FF8000000000001};
+ double nan_double1 = bit_cast<double>(nan_bits);
+ double nan_double2 = bit_cast<double>(nan_bits | 0x12300);
+ CHECK(std::isnan(nan_double1));
+ CHECK(std::isnan(nan_double2));
+ CHECK_NE(nan_double1, nan_double2);
+ CHECK_NE(bit_cast<uint64_t>(nan_double1), bit_cast<uint64_t>(nan_double2));
+
+ Handle<Object> nan1 = isolate->factory()->NewNumber(nan_double1);
+ Handle<Object> nan2 = isolate->factory()->NewNumber(nan_double2);
+
+ // NaNs with different bit patters are treated as equal upon stores.
+ const PropertyConstness kExpectedFieldConstness =
+ FLAG_track_constant_fields ? PropertyConstness::kConst
+ : PropertyConstness::kMutable;
+
+ TestStoreToConstantField(store_func_source, nan1, nan2,
+ Representation::Double(), kExpectedFieldConstness,
+ store_repetitions);
+}
+
+} // namespace
+
+TEST(StoreToConstantField_PlusMinusZero) {
+ FLAG_allow_natives_syntax = true;
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ const char* store_func_source =
+ "function store(o, v) {"
+ " %SetNamedProperty(o, 'v', v);"
+ "}";
+
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 1);
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 3);
+
+ TestStoreToConstantField_NaN(store_func_source, 1);
+ TestStoreToConstantField_NaN(store_func_source, 2);
+}
+
+TEST(StoreToConstantField_ObjectDefineProperty) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ const char* store_func_source =
+ "function store(o, v) {"
+ " Object.defineProperty(o, 'v', "
+ " {value: v, "
+ " writable: true, "
+ " configurable: true, "
+ " enumerable: true});"
+ "}";
+
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 1);
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 3);
+
+ TestStoreToConstantField_NaN(store_func_source, 1);
+ TestStoreToConstantField_NaN(store_func_source, 2);
+}
+
+TEST(StoreToConstantField_ReflectSet) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ const char* store_func_source =
+ "function store(o, v) {"
+ " Reflect.set(o, 'v', v);"
+ "}";
+
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 1);
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 3);
+
+ TestStoreToConstantField_NaN(store_func_source, 1);
+ TestStoreToConstantField_NaN(store_func_source, 2);
+}
+
+TEST(StoreToConstantField_StoreIC) {
+ CcTest::InitializeVM();
+ v8::HandleScope scope(CcTest::isolate());
+
+ const char* store_func_source =
+ "function store(o, v) {"
+ " o.v = v;"
+ "}";
+
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 1);
+ TestStoreToConstantField_PlusMinusZero(store_func_source, 3);
+
+ TestStoreToConstantField_NaN(store_func_source, 1);
+ TestStoreToConstantField_NaN(store_func_source, 2);
+}
+
} // namespace test_field_type_tracking
} // namespace compiler
} // namespace internal
diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-950747.js b/deps/v8/test/mjsunit/regress/regress-crbug-950747.js
new file mode 100644
index 0000000000..21a91bb5d8
--- /dev/null
+++ b/deps/v8/test/mjsunit/regress/regress-crbug-950747.js
@@ -0,0 +1,11 @@
+// Copyright 2019 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.
+
+let o = {};
+Reflect.set(o, "a", 0.1);
+
+let o1 = {};
+o1.a = {};
+
+Reflect.set(o, "a", 0.1);