From f2fe1e5a78d6decf04536764c8c4cf3f6c5f8dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Tue, 21 May 2019 08:21:34 +0200 Subject: [PATCH] deps: V8: cherry-pick 94c87fe Original commit message: [ic] Fix handling of +0/-0 when constant field tracking is enabled ... and ensure that runtime behaviour is in sync with the IC code. Bug: chromium:950747, v8:9113 Change-Id: Ied66c9514cbe3a4d75fc71d4fc3b19ea1538f9b2 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1561319 Reviewed-by: Toon Verwaest Commit-Queue: Igor Sheludko Cr-Commit-Position: refs/heads/master@{#60768} PR-URL: https://github.com/nodejs/node/pull/27792 Fixes: https://github.com/nodejs/node/issues/27784 Refs: https://github.com/v8/v8/commit/94c87fe0746fc95618ae091351f2f8c342212917 Reviewed-By: Ruben Bridgewater Reviewed-By: Ben Noordhuis Reviewed-By: Anna Henningsen Reviewed-By: Gus Caplan --- common.gypi | 2 +- deps/v8/src/code-stub-assembler.cc | 97 +++++---- deps/v8/src/code-stub-assembler.h | 9 +- deps/v8/src/ic/accessor-assembler.cc | 46 +++-- deps/v8/src/lookup.cc | 8 +- deps/v8/src/objects-inl.h | 10 + deps/v8/src/objects.cc | 48 +++-- deps/v8/src/objects.h | 4 + .../test/cctest/test-field-type-tracking.cc | 186 ++++++++++++++++++ .../mjsunit/regress/regress-crbug-950747.js | 11 ++ 10 files changed, 328 insertions(+), 93 deletions(-) create mode 100644 deps/v8/test/mjsunit/regress/regress-crbug-950747.js diff --git a/common.gypi b/common.gypi index 83efbb8898956b..223043e55194c3 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 e4dba15750e282..8f4930e560017e 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 lhs_value = UncheckedCast(var_lhs_value.value()); + TNode rhs_value = UncheckedCast(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 lhs_value, + TNode 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 86cc275c1450f4..c90b5d0e7e49f6 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 lhs_value, + TNode 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 bc636e41643bcb..fba761725be12e 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 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 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 current_value = + LoadObjectField(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 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 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 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 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 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(handler_word); - Node* offset = IntPtrMul(index, IntPtrConstant(kTaggedSize)); + TNode 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 current_value = + LoadObjectField(CAST(property_storage), offset); + BranchIfSameNumberValue(current_value, UncheckedCast(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 cc5d13dd6b1c8e..0e8a202a07890e 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(bits) == value->Number(); + return Object::SameNumberValue(bit_cast(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 6f146241b13ee2..33b2cb9bca3033 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 8337b3d4cf47ba..98b838e478d0a2 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 x, Handle y) { - return NumberEquals(*x, *y); +bool StrictNumberEquals(Handle x, Handle y) { + return StrictNumberEquals(*x, *y); } ComparisonResult Reverse(ComparisonResult result) { @@ -663,7 +664,7 @@ Maybe Object::Compare(Isolate* isolate, Handle 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::cast(x), Handle::cast(y))); @@ -683,11 +684,12 @@ Maybe Object::Equals(Isolate* isolate, Handle 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::cast(y)->to_number())); + return Just( + StrictNumberEquals(*x, Handle::cast(y)->to_number())); } else if (y->IsString()) { - return Just(NumberEquals( + return Just(StrictNumberEquals( x, String::ToNumber(isolate, Handle::cast(y)))); } else if (y->IsBigInt()) { return Just(BigInt::EqualToNumber(Handle::cast(y), x)); @@ -705,10 +707,11 @@ Maybe Object::Equals(Isolate* isolate, Handle x, Handle::cast(y))); } else if (y->IsNumber()) { x = String::ToNumber(isolate, Handle::cast(x)); - return Just(NumberEquals(x, y)); + return Just(StrictNumberEquals(x, y)); } else if (y->IsBoolean()) { x = String::ToNumber(isolate, Handle::cast(x)); - return Just(NumberEquals(*x, Handle::cast(y)->to_number())); + return Just( + StrictNumberEquals(*x, Handle::cast(y)->to_number())); } else if (y->IsBigInt()) { return Just(BigInt::EqualToString(isolate, Handle::cast(y), Handle::cast(x))); @@ -724,10 +727,12 @@ Maybe Object::Equals(Isolate* isolate, Handle x, if (y->IsOddball()) { return Just(x.is_identical_to(y)); } else if (y->IsNumber()) { - return Just(NumberEquals(Handle::cast(x)->to_number(), *y)); + return Just( + StrictNumberEquals(Handle::cast(x)->to_number(), *y)); } else if (y->IsString()) { y = String::ToNumber(isolate, Handle::cast(y)); - return Just(NumberEquals(Handle::cast(x)->to_number(), *y)); + return Just( + StrictNumberEquals(Handle::cast(x)->to_number(), *y)); } else if (y->IsBigInt()) { x = Oddball::ToNumber(isolate, Handle::cast(x)); return Just(BigInt::EqualToNumber(Handle::cast(y), x)); @@ -776,7 +781,7 @@ Maybe Object::Equals(Isolate* isolate, Handle 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 d7cb4f39d0e1e2..6c0f313261d1e4 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 49b0f920115400..0253a16735c312 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 +MaybeHandle Call(Isolate* isolate, Handle function, + Args... args) { + Handle argv[] = {args...}; + return Execution::Call(isolate, function, + isolate->factory()->undefined_value(), sizeof...(args), + argv); +} + +void TestStoreToConstantField(const char* store_func_source, + Handle value1, Handle value2, + Representation expected_rep, + PropertyConstness expected_constness, + int store_repetitions) { + Isolate* isolate = CcTest::i_isolate(); + CompileRun(store_func_source); + + Handle store_func = GetGlobal("store"); + + const PropertyConstness kExpectedInitialFieldConstness = + FLAG_track_constant_fields ? PropertyConstness::kConst + : PropertyConstness::kMutable; + + Handle initial_map = Map::Create(isolate, 4); + + // Store value1 to obj1 and check that it got property with expected + // representation and constness. + Handle obj1 = isolate->factory()->NewJSObjectFromMap(initial_map); + for (int i = 0; i < store_repetitions; i++) { + Call(isolate, store_func, obj1, value1).Check(); + } + + Handle 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 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 minus_zero = isolate->factory()->NewNumber(-0.0); + Handle 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(nan_bits); + double nan_double2 = bit_cast(nan_bits | 0x12300); + CHECK(std::isnan(nan_double1)); + CHECK(std::isnan(nan_double2)); + CHECK_NE(nan_double1, nan_double2); + CHECK_NE(bit_cast(nan_double1), bit_cast(nan_double2)); + + Handle nan1 = isolate->factory()->NewNumber(nan_double1); + Handle 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 00000000000000..21a91bb5d8797f --- /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);