Commit 6a597c67 authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[runtime] Fix Object.assign for in-place repr changes

Fix uses of cached descriptors arrays used in loops that map-check
to ensure validity of the cache to also reload the descriptor in
case there are missed in-place representation updates.

As a drive-by, introduce inner HandleScopes for these loops.

Bug: chromium:1012301
Change-Id: I17273caf629a181b846d3c09777b5c08fd8cbb0e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1859621Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64287}
parent 64c09f67
...@@ -8507,7 +8507,7 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8507,7 +8507,7 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
TNode<Uint16T> type = LoadMapInstanceType(map); TNode<Uint16T> type = LoadMapInstanceType(map);
TNode<Uint32T> bit_field3 = EnsureOnlyHasSimpleProperties(map, type, bailout); TNode<Uint32T> bit_field3 = EnsureOnlyHasSimpleProperties(map, type, bailout);
TNode<DescriptorArray> descriptors = LoadMapDescriptors(map); TVARIABLE(DescriptorArray, var_descriptors, LoadMapDescriptors(map));
TNode<Uint32T> nof_descriptors = TNode<Uint32T> nof_descriptors =
DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bit_field3); DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bit_field3);
...@@ -8522,13 +8522,14 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8522,13 +8522,14 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
// Note: var_end_key_index is exclusive for the loop // Note: var_end_key_index is exclusive for the loop
TVARIABLE(IntPtrT, var_end_key_index, TVARIABLE(IntPtrT, var_end_key_index,
ToKeyIndex<DescriptorArray>(nof_descriptors)); ToKeyIndex<DescriptorArray>(nof_descriptors));
VariableList list( VariableList list({&var_descriptors, &var_stable, &var_has_symbol,
{&var_stable, &var_has_symbol, &var_is_symbol_processing_loop, &var_is_symbol_processing_loop, &var_start_key_index,
&var_start_key_index, &var_end_key_index}, &var_end_key_index},
zone()); zone());
Label descriptor_array_loop( Label descriptor_array_loop(
this, {&var_stable, &var_has_symbol, &var_is_symbol_processing_loop, this, {&var_descriptors, &var_stable, &var_has_symbol,
&var_start_key_index, &var_end_key_index}); &var_is_symbol_processing_loop, &var_start_key_index,
&var_end_key_index});
Goto(&descriptor_array_loop); Goto(&descriptor_array_loop);
BIND(&descriptor_array_loop); BIND(&descriptor_array_loop);
...@@ -8537,7 +8538,7 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8537,7 +8538,7 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
list, var_start_key_index.value(), var_end_key_index.value(), list, var_start_key_index.value(), var_end_key_index.value(),
[&](TNode<IntPtrT> descriptor_key_index) { [&](TNode<IntPtrT> descriptor_key_index) {
TNode<Name> next_key = TNode<Name> next_key =
LoadKeyByKeyIndex(descriptors, descriptor_key_index); LoadKeyByKeyIndex(var_descriptors.value(), descriptor_key_index);
TVARIABLE(Object, var_value, SmiConstant(0)); TVARIABLE(Object, var_value, SmiConstant(0));
Label callback(this), next_iteration(this); Label callback(this), next_iteration(this);
...@@ -8592,7 +8593,7 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8592,7 +8593,7 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
// Directly decode from the descriptor array if |object| did not // Directly decode from the descriptor array if |object| did not
// change shape. // change shape.
var_map = map; var_map = map;
var_meta_storage = descriptors; var_meta_storage = var_descriptors.value();
var_entry = Signed(descriptor_key_index); var_entry = Signed(descriptor_key_index);
Goto(&if_found_fast); Goto(&if_found_fast);
} }
...@@ -8658,12 +8659,14 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8658,12 +8659,14 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
BIND(&callback); BIND(&callback);
body(next_key, var_value.value()); body(next_key, var_value.value());
// Check if |object| is still stable, i.e. we can proceed using // Check if |object| is still stable, i.e. the descriptors in the
// property details from preloaded |descriptors|. // preloaded |descriptors| are still the same modulo in-place
var_stable = Select<BoolT>( // representation changes.
var_stable.value(), GotoIfNot(var_stable.value(), &next_iteration);
[=] { return TaggedEqual(LoadMap(object), map); }, var_stable = TaggedEqual(LoadMap(object), map);
[=] { return Int32FalseConstant(); }); // Reload the descriptors just in case the actual array changed, and
// any of the field representations changed in-place.
var_descriptors = LoadMapDescriptors(map);
Goto(&next_iteration); Goto(&next_iteration);
} }
......
...@@ -220,10 +220,15 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign( ...@@ -220,10 +220,15 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
bool stable = true; bool stable = true;
for (InternalIndex i : map->IterateOwnDescriptors()) { for (InternalIndex i : map->IterateOwnDescriptors()) {
HandleScope inner_scope(isolate);
Handle<Name> next_key(descriptors->GetKey(i), isolate); Handle<Name> next_key(descriptors->GetKey(i), isolate);
Handle<Object> prop_value; Handle<Object> prop_value;
// Directly decode from the descriptor array if |from| did not change shape. // Directly decode from the descriptor array if |from| did not change shape.
if (stable) { if (stable) {
DCHECK_EQ(from->map(), *map);
DCHECK_EQ(*descriptors, map->instance_descriptors());
PropertyDetails details = descriptors->GetDetails(i); PropertyDetails details = descriptors->GetDetails(i);
if (!details.IsEnumerable()) continue; if (!details.IsEnumerable()) continue;
if (details.kind() == kData) { if (details.kind() == kData) {
...@@ -231,7 +236,8 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign( ...@@ -231,7 +236,8 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
prop_value = handle(descriptors->GetStrongValue(i), isolate); prop_value = handle(descriptors->GetStrongValue(i), isolate);
} else { } else {
Representation representation = details.representation(); Representation representation = details.representation();
FieldIndex index = FieldIndex::ForDescriptor(*map, i); FieldIndex index = FieldIndex::ForPropertyIndex(
*map, details.field_index(), representation);
prop_value = JSObject::FastPropertyAt(from, representation, index); prop_value = JSObject::FastPropertyAt(from, representation, index);
} }
} else { } else {
...@@ -239,6 +245,7 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign( ...@@ -239,6 +245,7 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
isolate, prop_value, isolate, prop_value,
JSReceiver::GetProperty(isolate, from, next_key), Nothing<bool>()); JSReceiver::GetProperty(isolate, from, next_key), Nothing<bool>());
stable = from->map() == *map; stable = from->map() == *map;
*descriptors.location() = map->instance_descriptors().ptr();
} }
} else { } else {
// If the map did change, do a slower lookup. We are still guaranteed that // If the map did change, do a slower lookup. We are still guaranteed that
...@@ -259,7 +266,10 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign( ...@@ -259,7 +266,10 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(
Object::SetProperty(&it, prop_value, StoreOrigin::kNamed, Object::SetProperty(&it, prop_value, StoreOrigin::kNamed,
Just(ShouldThrow::kThrowOnError)); Just(ShouldThrow::kThrowOnError));
if (result.IsNothing()) return result; if (result.IsNothing()) return result;
if (stable) stable = from->map() == *map; if (stable) {
stable = from->map() == *map;
*descriptors.location() = map->instance_descriptors().ptr();
}
} else { } else {
if (excluded_properties != nullptr && if (excluded_properties != nullptr &&
HasExcludedProperty(excluded_properties, next_key)) { HasExcludedProperty(excluded_properties, next_key)) {
...@@ -1832,8 +1842,8 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries( ...@@ -1832,8 +1842,8 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
if (!map->OnlyHasSimpleProperties()) return Just(false); if (!map->OnlyHasSimpleProperties()) return Just(false);
Handle<JSObject> object(JSObject::cast(*receiver), isolate); Handle<JSObject> object(JSObject::cast(*receiver), isolate);
Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate); Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate);
int number_of_own_descriptors = map->NumberOfOwnDescriptors(); int number_of_own_descriptors = map->NumberOfOwnDescriptors();
int number_of_own_elements = int number_of_own_elements =
object->GetElementsAccessor()->GetCapacity(*object, object->elements()); object->GetElementsAccessor()->GetCapacity(*object, object->elements());
...@@ -1855,15 +1865,25 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries( ...@@ -1855,15 +1865,25 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
Nothing<bool>()); Nothing<bool>());
} }
bool stable = object->map() == *map; // We may have already lost stability, if CollectValuesOrEntries had
// side-effects.
bool stable = *map == object->map();
if (stable) {
*descriptors.location() = map->instance_descriptors().ptr();
}
for (InternalIndex index : InternalIndex::Range(number_of_own_descriptors)) { for (InternalIndex index : InternalIndex::Range(number_of_own_descriptors)) {
HandleScope inner_scope(isolate);
Handle<Name> next_key(descriptors->GetKey(index), isolate); Handle<Name> next_key(descriptors->GetKey(index), isolate);
if (!next_key->IsString()) continue; if (!next_key->IsString()) continue;
Handle<Object> prop_value; Handle<Object> prop_value;
// Directly decode from the descriptor array if |from| did not change shape. // Directly decode from the descriptor array if |from| did not change shape.
if (stable) { if (stable) {
DCHECK_EQ(object->map(), *map);
DCHECK_EQ(*descriptors, map->instance_descriptors());
PropertyDetails details = descriptors->GetDetails(index); PropertyDetails details = descriptors->GetDetails(index);
if (!details.IsEnumerable()) continue; if (!details.IsEnumerable()) continue;
if (details.kind() == kData) { if (details.kind() == kData) {
...@@ -1871,7 +1891,8 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries( ...@@ -1871,7 +1891,8 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
prop_value = handle(descriptors->GetStrongValue(index), isolate); prop_value = handle(descriptors->GetStrongValue(index), isolate);
} else { } else {
Representation representation = details.representation(); Representation representation = details.representation();
FieldIndex field_index = FieldIndex::ForDescriptor(*map, index); FieldIndex field_index = FieldIndex::ForPropertyIndex(
*map, details.field_index(), representation);
prop_value = prop_value =
JSObject::FastPropertyAt(object, representation, field_index); JSObject::FastPropertyAt(object, representation, field_index);
} }
...@@ -1881,6 +1902,7 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries( ...@@ -1881,6 +1902,7 @@ V8_WARN_UNUSED_RESULT Maybe<bool> FastGetOwnValuesOrEntries(
JSReceiver::GetProperty(isolate, object, next_key), JSReceiver::GetProperty(isolate, object, next_key),
Nothing<bool>()); Nothing<bool>());
stable = object->map() == *map; stable = object->map() == *map;
*descriptors.location() = map->instance_descriptors().ptr();
} }
} else { } else {
// If the map did change, do a slower lookup. We are still guaranteed that // If the map did change, do a slower lookup. We are still guaranteed that
......
// 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.
function get() {
// Update the descriptor array now shared between the Foo map and the
// (Foo + c) map.
o1.c = 10;
// Change the type of the field on the new descriptor array in-place to
// Tagged. If Object.assign has a cached descriptor array, then it will point
// to the old Foo map's descriptors, which still have .b as Double.
o2.b = "string";
return 1;
}
function Foo() {
Object.defineProperty(this, "a", {get, enumerable: true});
// Initialise Foo.b to have Double representation.
this.b = 1.5;
}
var o1 = new Foo();
var o2 = new Foo();
var target = {};
Object.assign(target, o2);
// Make sure that target has the right representation after assignment.
assertEquals(target.b, "string");
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