[runtime] Add MapUpdater class that manages all kinds of map updates.

... including property reconfiguring, elements kind change and migration
of a map to an up-to-date non-deprecated version.

BUG=v8:5495

Review-Url: https://codereview.chromium.org/2601643002
Cr-Commit-Position: refs/heads/master@{#42177}
This commit is contained in:
ishell 2017-01-10 04:42:53 -08:00 committed by Commit bot
parent 8dfea24e3d
commit 0f159f5973
11 changed files with 857 additions and 669 deletions

View File

@ -1539,6 +1539,8 @@ v8_source_set("v8_base") {
"src/machine-type.cc",
"src/machine-type.h",
"src/macro-assembler.h",
"src/map-updater.cc",
"src/map-updater.h",
"src/messages.cc",
"src/messages.h",
"src/msan.h",

View File

@ -399,8 +399,8 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
->NowContains(value)) {
Handle<FieldType> value_type(
value->OptimalType(isolate(), expected_representation));
Map::GeneralizeFieldType(target, descriptor,
expected_representation, value_type);
Map::GeneralizeField(target, descriptor, expected_representation,
value_type);
}
DCHECK(target->instance_descriptors()
->GetFieldType(descriptor)

615
src/map-updater.cc Normal file
View File

@ -0,0 +1,615 @@
// 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.
#include "src/map-updater.h"
#include "src/field-type.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "src/transitions.h"
namespace v8 {
namespace internal {
namespace {
inline bool EqualImmutableValues(Object* obj1, Object* obj2) {
if (obj1 == obj2) return true; // Valid for both kData and kAccessor kinds.
// TODO(ishell): compare AccessorPairs.
return false;
}
} // namespace
Name* MapUpdater::GetKey(int descriptor) const {
return old_descriptors_->GetKey(descriptor);
}
PropertyDetails MapUpdater::GetDetails(int descriptor) const {
DCHECK_LE(0, descriptor);
if (descriptor == modified_descriptor_) {
return PropertyDetails(new_kind_, new_attributes_, new_location_,
new_representation_);
}
return old_descriptors_->GetDetails(descriptor);
}
Object* MapUpdater::GetValue(int descriptor) const {
DCHECK_LE(0, descriptor);
if (descriptor == modified_descriptor_) {
DCHECK_EQ(kDescriptor, new_location_);
return *new_value_;
}
DCHECK_EQ(kDescriptor, GetDetails(descriptor).location());
return old_descriptors_->GetValue(descriptor);
}
FieldType* MapUpdater::GetFieldType(int descriptor) const {
DCHECK_LE(0, descriptor);
if (descriptor == modified_descriptor_) {
DCHECK_EQ(kField, new_location_);
return *new_field_type_;
}
DCHECK_EQ(kField, GetDetails(descriptor).location());
return old_descriptors_->GetFieldType(descriptor);
}
Handle<FieldType> MapUpdater::GetOrComputeFieldType(
int descriptor, PropertyLocation location,
Representation representation) const {
DCHECK_LE(0, descriptor);
// |location| is just a pre-fetched GetDetails(descriptor).location().
DCHECK_EQ(location, GetDetails(descriptor).location());
if (location == kField) {
return handle(GetFieldType(descriptor), isolate_);
} else {
return GetValue(descriptor)->OptimalType(isolate_, representation);
}
}
Handle<FieldType> MapUpdater::GetOrComputeFieldType(
Handle<DescriptorArray> descriptors, int descriptor,
PropertyLocation location, Representation representation) {
// |location| is just a pre-fetched GetDetails(descriptor).location().
DCHECK_EQ(descriptors->GetDetails(descriptor).location(), location);
if (location == kField) {
return handle(descriptors->GetFieldType(descriptor), isolate_);
} else {
return descriptors->GetValue(descriptor)
->OptimalType(isolate_, representation);
}
}
Handle<Map> MapUpdater::ReconfigureToDataField(int descriptor,
PropertyAttributes attributes,
Representation representation,
Handle<FieldType> field_type) {
DCHECK_EQ(kInitialized, state_);
DCHECK_LE(0, descriptor);
DCHECK(!old_map_->is_dictionary_map());
modified_descriptor_ = descriptor;
new_kind_ = kData;
new_attributes_ = attributes;
new_location_ = kField;
new_representation_ = representation;
new_field_type_ = field_type;
PropertyDetails old_details =
old_descriptors_->GetDetails(modified_descriptor_);
// If property kind is not reconfigured merge the result with
// representation/field type from the old descriptor.
if (old_details.kind() == new_kind_) {
Representation old_representation = old_details.representation();
new_representation_ = new_representation_.generalize(old_representation);
Handle<FieldType> old_field_type =
GetOrComputeFieldType(old_descriptors_, modified_descriptor_,
old_details.location(), new_representation_);
new_field_type_ = Map::GeneralizeFieldType(
old_representation, old_field_type, new_representation_,
new_field_type_, isolate_);
}
if (TryRecofigureToDataFieldInplace() == kEnd) return result_map_;
if (FindRootMap() == kEnd) return result_map_;
if (FindTargetMap() == kEnd) return result_map_;
ConstructNewMap();
DCHECK_EQ(kEnd, state_);
return result_map_;
}
Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) {
DCHECK_EQ(kInitialized, state_);
new_elements_kind_ = elements_kind;
if (FindRootMap() == kEnd) return result_map_;
if (FindTargetMap() == kEnd) return result_map_;
ConstructNewMap();
DCHECK_EQ(kEnd, state_);
return result_map_;
}
Handle<Map> MapUpdater::Update() {
DCHECK_EQ(kInitialized, state_);
DCHECK(old_map_->is_deprecated());
if (FindRootMap() == kEnd) return result_map_;
if (FindTargetMap() == kEnd) return result_map_;
ConstructNewMap();
DCHECK_EQ(kEnd, state_);
return result_map_;
}
MapUpdater::State MapUpdater::CopyGeneralizeAllRepresentations(
const char* reason) {
StoreMode store_mode =
modified_descriptor_ >= 0 ? FORCE_FIELD : ALLOW_IN_DESCRIPTOR;
result_map_ = Map::CopyGeneralizeAllRepresentations(
old_map_, new_elements_kind_, modified_descriptor_, store_mode, new_kind_,
new_attributes_, reason);
state_ = kEnd;
return state_; // Done.
}
MapUpdater::State MapUpdater::TryRecofigureToDataFieldInplace() {
// If it's just a representation generalization case (i.e. property kind and
// attributes stays unchanged) it's fine to transition from None to anything
// but double without any modification to the object, because the default
// uninitialized value for representation None can be overwritten by both
// smi and tagged values. Doubles, however, would require a box allocation.
if (new_representation_.IsNone() || new_representation_.IsDouble()) {
return state_; // Not done yet.
}
PropertyDetails old_details =
old_descriptors_->GetDetails(modified_descriptor_);
Representation old_representation = old_details.representation();
if (!old_representation.IsNone()) {
return state_; // Not done yet.
}
DCHECK_EQ(new_kind_, old_details.kind());
DCHECK_EQ(new_attributes_, old_details.attributes());
DCHECK_EQ(kField, old_details.location());
if (FLAG_trace_generalization) {
old_map_->PrintGeneralization(
stdout, "uninitialized field", modified_descriptor_, old_nof_, old_nof_,
false, old_representation, new_representation_,
handle(old_descriptors_->GetFieldType(modified_descriptor_), isolate_),
MaybeHandle<Object>(), new_field_type_, MaybeHandle<Object>());
}
Handle<Map> field_owner(old_map_->FindFieldOwner(modified_descriptor_),
isolate_);
Map::GeneralizeField(field_owner, modified_descriptor_, new_representation_,
new_field_type_);
// Check that the descriptor array was updated.
DCHECK(old_descriptors_->GetDetails(modified_descriptor_)
.representation()
.Equals(new_representation_));
DCHECK(old_descriptors_->GetFieldType(modified_descriptor_)
->NowIs(new_field_type_));
result_map_ = old_map_;
state_ = kEnd;
return state_; // Done.
}
MapUpdater::State MapUpdater::FindRootMap() {
DCHECK_EQ(kInitialized, state_);
// Check the state of the root map.
root_map_ = handle(old_map_->FindRootMap(), isolate_);
int root_nof = root_map_->NumberOfOwnDescriptors();
if (!old_map_->EquivalentToForTransition(*root_map_)) {
return CopyGeneralizeAllRepresentations("GenAll_NotEquivalent");
}
ElementsKind from_kind = root_map_->elements_kind();
ElementsKind to_kind = new_elements_kind_;
// TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
!(IsTransitionableFastElementsKind(from_kind) &&
IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
return CopyGeneralizeAllRepresentations("GenAll_InvalidElementsTransition");
}
if (modified_descriptor_ >= 0 && modified_descriptor_ < root_nof) {
PropertyDetails old_details =
old_descriptors_->GetDetails(modified_descriptor_);
if (old_details.kind() != new_kind_ ||
old_details.attributes() != new_attributes_) {
return CopyGeneralizeAllRepresentations("GenAll_RootModification1");
}
if (!new_representation_.fits_into(old_details.representation())) {
return CopyGeneralizeAllRepresentations("GenAll_RootModification2");
}
if (old_details.location() != kField) {
return CopyGeneralizeAllRepresentations("GenAll_RootModification3");
}
DCHECK_EQ(kData, old_details.kind());
DCHECK_EQ(kData, new_kind_);
DCHECK_EQ(kField, new_location_);
FieldType* old_field_type =
old_descriptors_->GetFieldType(modified_descriptor_);
if (!new_field_type_->NowIs(old_field_type)) {
return CopyGeneralizeAllRepresentations("GenAll_RootModification4");
}
}
// From here on, use the map with correct elements kind as root map.
if (from_kind != to_kind) {
root_map_ = Map::AsElementsKind(root_map_, to_kind);
}
state_ = kAtRootMap;
return state_; // Not done yet.
}
MapUpdater::State MapUpdater::FindTargetMap() {
DCHECK_EQ(kAtRootMap, state_);
target_map_ = root_map_;
int root_nof = root_map_->NumberOfOwnDescriptors();
for (int i = root_nof; i < old_nof_; ++i) {
PropertyDetails old_details = GetDetails(i);
Map* transition = TransitionArray::SearchTransition(
*target_map_, old_details.kind(), GetKey(i), old_details.attributes());
if (transition == NULL) break;
Handle<Map> tmp_map(transition, isolate_);
Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
isolate_);
// Check if target map is incompatible.
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
DCHECK_EQ(old_details.kind(), tmp_details.kind());
DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
if (old_details.kind() == kAccessor &&
!EqualImmutableValues(GetValue(i), tmp_descriptors->GetValue(i))) {
// TODO(ishell): mutable accessors are not implemented yet.
return CopyGeneralizeAllRepresentations("GenAll_Incompatible");
}
// Check if old location fits into tmp location.
if (old_details.location() == kField &&
tmp_details.location() == kDescriptor) {
break;
}
// Check if old representation fits into tmp representation.
Representation tmp_representation = tmp_details.representation();
if (!old_details.representation().fits_into(tmp_representation)) {
break;
}
if (tmp_details.location() == kField) {
Handle<FieldType> old_field_type =
GetOrComputeFieldType(i, old_details.location(), tmp_representation);
Map::GeneralizeField(tmp_map, i, tmp_representation, old_field_type);
} else {
// kDescriptor: Check that the value matches.
if (!EqualImmutableValues(GetValue(i), tmp_descriptors->GetValue(i))) {
break;
}
}
DCHECK(!tmp_map->is_deprecated());
target_map_ = tmp_map;
}
// Directly change the map if the target map is more general.
int target_nof = target_map_->NumberOfOwnDescriptors();
if (target_nof == old_nof_) {
#ifdef DEBUG
if (modified_descriptor_ >= 0) {
DescriptorArray* target_descriptors = target_map_->instance_descriptors();
PropertyDetails details =
target_descriptors->GetDetails(modified_descriptor_);
DCHECK_EQ(new_kind_, details.kind());
DCHECK_EQ(new_attributes_, details.attributes());
DCHECK_EQ(new_location_, details.location());
DCHECK(new_representation_.fits_into(details.representation()));
if (new_location_ == kField) {
DCHECK_EQ(kField, details.location());
DCHECK(new_field_type_->NowIs(
target_descriptors->GetFieldType(modified_descriptor_)));
} else {
DCHECK(details.location() == kField ||
EqualImmutableValues(*new_value_, target_descriptors->GetValue(
modified_descriptor_)));
}
}
#endif
if (*target_map_ != *old_map_) {
old_map_->NotifyLeafMapLayoutChange();
}
result_map_ = target_map_;
state_ = kEnd;
return state_; // Done.
}
// Find the last compatible target map in the transition tree.
for (int i = target_nof; i < old_nof_; ++i) {
PropertyDetails old_details = GetDetails(i);
Map* transition = TransitionArray::SearchTransition(
*target_map_, old_details.kind(), GetKey(i), old_details.attributes());
if (transition == NULL) break;
Handle<Map> tmp_map(transition, isolate_);
Handle<DescriptorArray> tmp_descriptors(tmp_map->instance_descriptors(),
isolate_);
#ifdef DEBUG
// Check that target map is compatible.
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
DCHECK_EQ(old_details.kind(), tmp_details.kind());
DCHECK_EQ(old_details.attributes(), tmp_details.attributes());
#endif
if (old_details.kind() == kAccessor &&
!EqualImmutableValues(GetValue(i), tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations("GenAll_Incompatible");
}
DCHECK(!tmp_map->is_deprecated());
target_map_ = tmp_map;
}
state_ = kAtTargetMap;
return state_; // Not done yet.
}
Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
int target_nof = target_map_->NumberOfOwnDescriptors();
Handle<DescriptorArray> target_descriptors(
target_map_->instance_descriptors(), isolate_);
// Allocate a new descriptor array large enough to hold the required
// descriptors, with minimally the exact same size as the old descriptor
// array.
int new_slack =
Max(old_nof_, old_descriptors_->number_of_descriptors()) - old_nof_;
Handle<DescriptorArray> new_descriptors =
DescriptorArray::Allocate(isolate_, old_nof_, new_slack);
DCHECK(new_descriptors->length() > target_descriptors->length() ||
new_descriptors->NumberOfSlackDescriptors() > 0 ||
new_descriptors->number_of_descriptors() ==
old_descriptors_->number_of_descriptors());
DCHECK(new_descriptors->number_of_descriptors() == old_nof_);
int root_nof = root_map_->NumberOfOwnDescriptors();
// Given that we passed root modification check in FindRootMap() so
// the root descriptors are either not modified at all or already more
// general than we requested. Take |root_nof| entries as is.
// 0 -> |root_nof|
int current_offset = 0;
for (int i = 0; i < root_nof; ++i) {
PropertyDetails old_details = old_descriptors_->GetDetails(i);
if (old_details.location() == kField) {
current_offset += old_details.field_width_in_words();
}
Descriptor d(handle(GetKey(i), isolate_),
handle(old_descriptors_->GetValue(i), isolate_), old_details);
new_descriptors->Set(i, &d);
}
// Merge "updated" old_descriptor entries with target_descriptor entries.
// |root_nof| -> |target_nof|
for (int i = root_nof; i < target_nof; ++i) {
Handle<Name> key(GetKey(i), isolate_);
PropertyDetails old_details = GetDetails(i);
PropertyDetails target_details = target_descriptors->GetDetails(i);
PropertyKind next_kind = old_details.kind();
PropertyAttributes next_attributes = old_details.attributes();
PropertyLocation next_location =
old_details.location() == kField ||
target_details.location() == kField ||
!EqualImmutableValues(target_descriptors->GetValue(i),
GetValue(i))
? kField
: kDescriptor;
Representation next_representation =
old_details.representation().generalize(
target_details.representation());
DCHECK_EQ(next_kind, target_details.kind());
DCHECK_EQ(next_attributes, target_details.attributes());
if (next_location == kField) {
Handle<FieldType> old_field_type =
GetOrComputeFieldType(i, old_details.location(), next_representation);
Handle<FieldType> target_field_type =
GetOrComputeFieldType(target_descriptors, i,
target_details.location(), next_representation);
Handle<FieldType> next_field_type = Map::GeneralizeFieldType(
old_details.representation(), old_field_type, next_representation,
target_field_type, isolate_);
Handle<Object> wrapped_type(Map::WrapType(next_field_type));
Descriptor d;
if (next_kind == kData) {
d = Descriptor::DataField(key, current_offset, wrapped_type,
next_attributes, next_representation);
} else {
// TODO(ishell): mutable accessors are not implemented yet.
UNIMPLEMENTED();
}
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
DCHECK_EQ(kDescriptor, next_location);
Handle<Object> value(GetValue(i), isolate_);
Descriptor d;
if (next_kind == kData) {
d = Descriptor::DataConstant(key, value, next_attributes);
} else {
DCHECK_EQ(kAccessor, next_kind);
d = Descriptor::AccessorConstant(key, value, next_attributes);
}
new_descriptors->Set(i, &d);
}
}
// Take "updated" old_descriptor entries.
// |target_nof| -> |old_nof|
for (int i = target_nof; i < old_nof_; ++i) {
PropertyDetails old_details = GetDetails(i);
Handle<Name> key(GetKey(i), isolate_);
PropertyKind next_kind = old_details.kind();
PropertyAttributes next_attributes = old_details.attributes();
PropertyLocation next_location = old_details.location();
Representation next_representation = old_details.representation();
Descriptor d;
if (next_location == kField) {
Handle<FieldType> old_field_type =
GetOrComputeFieldType(i, old_details.location(), next_representation);
Handle<Object> wrapped_type(Map::WrapType(old_field_type));
Descriptor d;
if (next_kind == kData) {
d = Descriptor::DataField(key, current_offset, wrapped_type,
next_attributes, next_representation);
} else {
// TODO(ishell): mutable accessors are not implemented yet.
UNIMPLEMENTED();
}
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
DCHECK_EQ(kDescriptor, next_location);
Handle<Object> value(GetValue(i), isolate_);
if (next_kind == kData) {
d = Descriptor::DataConstant(key, value, next_attributes);
} else {
DCHECK_EQ(kAccessor, next_kind);
d = Descriptor::AccessorConstant(key, value, next_attributes);
}
new_descriptors->Set(i, &d);
}
}
new_descriptors->Sort();
return new_descriptors;
}
Handle<Map> MapUpdater::FindSplitMap(Handle<DescriptorArray> descriptors) {
DisallowHeapAllocation no_allocation;
int root_nof = root_map_->NumberOfOwnDescriptors();
Map* current = *root_map_;
for (int i = root_nof; i < old_nof_; i++) {
Name* name = descriptors->GetKey(i);
PropertyDetails details = descriptors->GetDetails(i);
Map* next = TransitionArray::SearchTransition(current, details.kind(), name,
details.attributes());
if (next == NULL) break;
DescriptorArray* next_descriptors = next->instance_descriptors();
PropertyDetails next_details = next_descriptors->GetDetails(i);
DCHECK_EQ(details.kind(), next_details.kind());
DCHECK_EQ(details.attributes(), next_details.attributes());
if (details.location() != next_details.location()) break;
if (!details.representation().Equals(next_details.representation())) break;
if (next_details.location() == kField) {
FieldType* next_field_type = next_descriptors->GetFieldType(i);
if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
break;
}
} else {
if (!EqualImmutableValues(descriptors->GetValue(i),
next_descriptors->GetValue(i))) {
break;
}
}
current = next;
}
return handle(current, isolate_);
}
MapUpdater::State MapUpdater::ConstructNewMap() {
Handle<DescriptorArray> new_descriptors = BuildDescriptorArray();
Handle<Map> split_map = FindSplitMap(new_descriptors);
int split_nof = split_map->NumberOfOwnDescriptors();
DCHECK_NE(old_nof_, split_nof);
PropertyDetails split_details = GetDetails(split_nof);
// Invalidate a transition target at |key|.
Map* maybe_transition = TransitionArray::SearchTransition(
*split_map, split_details.kind(), GetKey(split_nof),
split_details.attributes());
if (maybe_transition != NULL) {
maybe_transition->DeprecateTransitionTree();
}
// If |maybe_transition| is not NULL then the transition array already
// contains entry for given descriptor. This means that the transition
// could be inserted regardless of whether transitions array is full or not.
if (maybe_transition == NULL &&
!TransitionArray::CanHaveMoreTransitions(split_map)) {
return CopyGeneralizeAllRepresentations("GenAll_CantHaveMoreTransitions");
}
old_map_->NotifyLeafMapLayoutChange();
if (FLAG_trace_generalization && modified_descriptor_ >= 0) {
PropertyDetails old_details =
old_descriptors_->GetDetails(modified_descriptor_);
PropertyDetails new_details =
new_descriptors->GetDetails(modified_descriptor_);
MaybeHandle<FieldType> old_field_type;
MaybeHandle<FieldType> new_field_type;
MaybeHandle<Object> old_value;
MaybeHandle<Object> new_value;
if (old_details.type() == DATA) {
old_field_type = handle(
old_descriptors_->GetFieldType(modified_descriptor_), isolate_);
} else {
old_value =
handle(old_descriptors_->GetValue(modified_descriptor_), isolate_);
}
if (new_details.type() == DATA) {
new_field_type =
handle(new_descriptors->GetFieldType(modified_descriptor_), isolate_);
} else {
new_value =
handle(new_descriptors->GetValue(modified_descriptor_), isolate_);
}
old_map_->PrintGeneralization(
stdout, "", modified_descriptor_, split_nof, old_nof_,
old_details.location() == kDescriptor && new_location_ == kField,
old_details.representation(), new_details.representation(),
old_field_type, old_value, new_field_type, new_value);
}
Handle<LayoutDescriptor> new_layout_descriptor =
LayoutDescriptor::New(split_map, new_descriptors, old_nof_);
Handle<Map> new_map = Map::AddMissingTransitions(split_map, new_descriptors,
new_layout_descriptor);
// Deprecated part of the transition tree is no longer reachable, so replace
// current instance descriptors in the "survived" part of the tree with
// the new descriptors to maintain descriptors sharing invariant.
split_map->ReplaceDescriptors(*new_descriptors, *new_layout_descriptor);
result_map_ = new_map;
state_ = kEnd;
return state_; // Done.
}
} // namespace internal
} // namespace v8

173
src/map-updater.h Normal file
View File

@ -0,0 +1,173 @@
// 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.
#ifndef V8_MAP_RECONFIGURER_H_
#define V8_MAP_RECONFIGURER_H_
#include "src/elements-kind.h"
#include "src/globals.h"
#include "src/handles.h"
#include "src/objects.h"
#include "src/property-details.h"
namespace v8 {
namespace internal {
// The |MapUpdater| class implements all sorts of map reconfigurations
// including changes of elements kind, property attributes, property kind,
// property location and field representations/type changes. It ensures that
// the reconfigured map and all the intermediate maps are properly integrated
// into the exising transition tree.
//
// To avoid high degrees over polymorphism, and to stabilize quickly, on every
// rewrite the new type is deduced by merging the current type with any
// potential new (partial) version of the type in the transition tree.
// To do this, on each rewrite:
// - Search the root of the transition tree using FindRootMap.
// - Find/create a |root_map| with requested |new_elements_kind|.
// - Find |target_map|, the newest matching version of this map using the
// "updated" |old_map|'s descriptor array (i.e. whose entry at |modify_index|
// is considered to be of |new_kind| and having |new_attributes|) to walk
// the transition tree.
// - Merge/generalize the "updated" descriptor array of the |old_map| and
// descriptor array of the |target_map|.
// - Generalize the |modify_index| descriptor using |new_representation| and
// |new_field_type|.
// - Walk the tree again starting from the root towards |target_map|. Stop at
// |split_map|, the first map who's descriptor array does not match the merged
// descriptor array.
// - If |target_map| == |split_map|, |target_map| is in the expected state.
// Return it.
// - Otherwise, invalidate the outdated transition target from |target_map|, and
// replace its transition tree with a new branch for the updated descriptors.
class MapUpdater {
public:
MapUpdater(Isolate* isolate, Handle<Map> old_map)
: isolate_(isolate),
old_map_(old_map),
old_descriptors_(old_map->instance_descriptors(), isolate_),
old_nof_(old_map_->NumberOfOwnDescriptors()),
new_elements_kind_(old_map_->elements_kind()) {}
// Prepares for reconfiguring of a property at |descriptor| to data field
// with given |attributes| and |representation|/|field_type| and
// performs the steps 1-5.
Handle<Map> ReconfigureToDataField(int descriptor,
PropertyAttributes attributes,
Representation representation,
Handle<FieldType> field_type);
// Prepares for reconfiguring elements kind and performs the steps 1-5.
Handle<Map> ReconfigureElementsKind(ElementsKind elements_kind);
// Prepares for updating deprecated map to most up-to-date non-deprecated
// version and performs the steps 1-5.
Handle<Map> Update();
private:
enum State { kInitialized, kAtRootMap, kAtTargetMap, kEnd };
// Try to reconfigure property in-place without rebuilding transition tree
// and creating new maps. See implementation for details.
State TryRecofigureToDataFieldInplace();
// Step 1.
// - Search the root of the transition tree using FindRootMap.
// - Find/create a |root_map_| with requested |new_elements_kind_|.
State FindRootMap();
// Step 2.
// - Find |target_map_|, the newest matching version of this map using the
// "updated" |old_map|'s descriptor array (i.e. whose entry at
// |modified_descriptor_| is considered to be of |new_kind| and having
// |new_attributes|) to walk the transition tree.
State FindTargetMap();
// Step 3.
// - Merge/generalize the "updated" descriptor array of the |old_map_| and
// descriptor array of the |target_map_|.
// - Generalize the |modified_descriptor_| using |new_representation| and
// |new_field_type_|.
Handle<DescriptorArray> BuildDescriptorArray();
// Step 4.
// - Walk the tree again starting from the root towards |target_map|. Stop at
// |split_map|, the first map who's descriptor array does not match the
// merged descriptor array.
Handle<Map> FindSplitMap(Handle<DescriptorArray> descriptors);
// Step 5.
// - If |target_map| == |split_map|, |target_map| is in the expected state.
// Return it.
// - Otherwise, invalidate the outdated transition target from |target_map|,
// and replace its transition tree with a new branch for the updated
// descriptors.
State ConstructNewMap();
// When a requested reconfiguration can not be done the result is a copy
// of |old_map_| where every field has |Tagged| representation and |Any|
// field type. This map is disconnected from the transition tree.
State CopyGeneralizeAllRepresentations(const char* reason);
// Returns name of a |descriptor| property.
inline Name* GetKey(int descriptor) const;
// Returns property details of a |descriptor| in "updated" |old_descrtiptors_|
// array.
inline PropertyDetails GetDetails(int descriptor) const;
// Returns value of a |descriptor| with kDescriptor location in "updated"
// |old_descrtiptors_| array.
inline Object* GetValue(int descriptor) const;
// Returns field type for a |descriptor| with kField location in "updated"
// |old_descrtiptors_| array.
inline FieldType* GetFieldType(int descriptor) const;
// If a |descriptor| property in "updated" |old_descriptors_| has kField
// location then returns it's field type otherwise computes optimal field
// type for the descriptor's value and |representation|. The |location|
// value must be a pre-fetched location for |descriptor|.
inline Handle<FieldType> GetOrComputeFieldType(
int descriptor, PropertyLocation location,
Representation representation) const;
// If a |descriptor| property in given |descriptors| array has kField
// location then returns it's field type otherwise computes optimal field
// type for the descriptor's value and |representation|.
// The |location| value must be a pre-fetched location for |descriptor|.
inline Handle<FieldType> GetOrComputeFieldType(
Handle<DescriptorArray> descriptors, int descriptor,
PropertyLocation location, Representation representation);
Isolate* isolate_;
Handle<Map> old_map_;
Handle<DescriptorArray> old_descriptors_;
Handle<Map> root_map_;
Handle<Map> target_map_;
Handle<Map> result_map_;
int old_nof_;
State state_ = kInitialized;
ElementsKind new_elements_kind_;
// If |modified_descriptor_| is not equal to -1 them the fields below form
// an "update" of the |old_map_|'s descriptors.
int modified_descriptor_ = -1;
PropertyKind new_kind_ = kData;
PropertyAttributes new_attributes_ = NONE;
PropertyLocation new_location_ = kField;
Representation new_representation_ = Representation::None();
// Data specific to kField location.
Handle<FieldType> new_field_type_;
// Data specific to kDescriptor location.
Handle<Object> new_value_;
};
} // namespace internal
} // namespace v8
#endif // V8_MAP_RECONFIGURER_H_

View File

@ -3040,26 +3040,6 @@ FixedArrayBase* Map::GetInitialElements() {
return result;
}
// static
Handle<Map> Map::ReconfigureProperty(Handle<Map> map, int modify_index,
PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<FieldType> new_field_type,
StoreMode store_mode) {
return Reconfigure(map, map->elements_kind(), modify_index, new_kind,
new_attributes, new_representation, new_field_type,
store_mode);
}
// static
Handle<Map> Map::ReconfigureElementsKind(Handle<Map> map,
ElementsKind new_elements_kind) {
return Reconfigure(map, new_elements_kind, -1, kData, NONE,
Representation::None(), FieldType::None(map->GetIsolate()),
ALLOW_IN_DESCRIPTOR);
}
Object** DescriptorArray::GetKeySlot(int descriptor_number) {
DCHECK(descriptor_number < number_of_descriptors());
return RawFieldOfElementAt(ToKeyIndex(descriptor_number));

View File

@ -49,6 +49,7 @@
#include "src/log.h"
#include "src/lookup.h"
#include "src/macro-assembler.h"
#include "src/map-updater.h"
#include "src/messages.h"
#include "src/objects-body-descriptors-inl.h"
#include "src/property-descriptor.h"
@ -3326,7 +3327,7 @@ Context* JSReceiver::GetCreationContext() {
return function->context()->native_context();
}
static Handle<Object> WrapType(Handle<FieldType> type) {
Handle<Object> Map::WrapType(Handle<FieldType> type) {
if (type->IsClass()) return Map::WeakCellForMap(type->AsClass());
return type;
}
@ -3969,13 +3970,6 @@ void Map::DeprecateTransitionTree() {
}
static inline bool EqualImmutableValues(Object* obj1, Object* obj2) {
if (obj1 == obj2) return true; // Valid for both kData and kAccessor kinds.
// TODO(ishell): compare AccessorPairs.
return false;
}
// Installs |new_descriptors| over the current instance_descriptors to ensure
// proper sharing of descriptor arrays.
void Map::ReplaceDescriptors(DescriptorArray* new_descriptors,
@ -4018,47 +4012,6 @@ Map* Map::FindRootMap() {
}
Map* Map::FindLastMatchMap(int verbatim,
int length,
DescriptorArray* descriptors) {
DisallowHeapAllocation no_allocation;
// This can only be called on roots of transition trees.
DCHECK_EQ(verbatim, NumberOfOwnDescriptors());
Map* current = this;
for (int i = verbatim; i < length; i++) {
Name* name = descriptors->GetKey(i);
PropertyDetails details = descriptors->GetDetails(i);
Map* next = TransitionArray::SearchTransition(current, details.kind(), name,
details.attributes());
if (next == NULL) break;
DescriptorArray* next_descriptors = next->instance_descriptors();
PropertyDetails next_details = next_descriptors->GetDetails(i);
DCHECK_EQ(details.kind(), next_details.kind());
DCHECK_EQ(details.attributes(), next_details.attributes());
if (details.location() != next_details.location()) break;
if (!details.representation().Equals(next_details.representation())) break;
if (next_details.location() == kField) {
FieldType* next_field_type = next_descriptors->GetFieldType(i);
if (!descriptors->GetFieldType(i)->NowIs(next_field_type)) {
break;
}
} else {
if (!EqualImmutableValues(descriptors->GetValue(i),
next_descriptors->GetValue(i))) {
break;
}
}
current = next;
}
return current;
}
Map* Map::FindFieldOwner(int descriptor) {
DisallowHeapAllocation no_allocation;
DCHECK_EQ(DATA, instance_descriptors()->GetDetails(descriptor).type());
@ -4139,9 +4092,9 @@ Handle<FieldType> Map::GeneralizeFieldType(Representation rep1,
// static
void Map::GeneralizeFieldType(Handle<Map> map, int modify_index,
Representation new_representation,
Handle<FieldType> new_field_type) {
void Map::GeneralizeField(Handle<Map> map, int modify_index,
Representation new_representation,
Handle<FieldType> new_field_type) {
Isolate* isolate = map->GetIsolate();
// Check if we actually need to generalize the field type at all.
@ -4156,8 +4109,8 @@ void Map::GeneralizeFieldType(Handle<Map> map, int modify_index,
// Checking old_field_type for being cleared is not necessary because
// the NowIs check below would fail anyway in that case.
new_field_type->NowIs(old_field_type)) {
DCHECK(Map::GeneralizeFieldType(old_representation, old_field_type,
new_representation, new_field_type, isolate)
DCHECK(GeneralizeFieldType(old_representation, old_field_type,
new_representation, new_field_type, isolate)
->NowIs(old_field_type));
return;
}
@ -4190,579 +4143,41 @@ void Map::GeneralizeFieldType(Handle<Map> map, int modify_index,
}
}
static inline Handle<FieldType> GetFieldType(
Isolate* isolate, Handle<DescriptorArray> descriptors, int descriptor,
PropertyLocation location, Representation representation) {
#ifdef DEBUG
PropertyDetails details = descriptors->GetDetails(descriptor);
DCHECK_EQ(kData, details.kind());
DCHECK_EQ(details.location(), location);
#endif
if (location == kField) {
return handle(descriptors->GetFieldType(descriptor), isolate);
} else {
return descriptors->GetValue(descriptor)
->OptimalType(isolate, representation);
}
// TODO(ishell): remove.
// static
Handle<Map> Map::ReconfigureProperty(Handle<Map> map, int modify_index,
PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<FieldType> new_field_type,
StoreMode store_mode) {
DCHECK_EQ(kData, new_kind); // Only kData case is supported.
MapUpdater mu(map->GetIsolate(), map);
return mu.ReconfigureToDataField(modify_index, new_attributes,
new_representation, new_field_type);
}
// Reconfigures elements kind to |new_elements_kind| and/or property at
// |modify_index| with |new_kind|, |new_attributes|, |store_mode| and/or
// |new_representation|/|new_field_type|.
// If |modify_index| is negative then no properties are reconfigured but the
// map is migrated to the up-to-date non-deprecated state.
//
// This method rewrites or completes the transition tree to reflect the new
// change. To avoid high degrees over polymorphism, and to stabilize quickly,
// on every rewrite the new type is deduced by merging the current type with
// any potential new (partial) version of the type in the transition tree.
// To do this, on each rewrite:
// - Search the root of the transition tree using FindRootMap.
// - Find/create a |root_map| with requested |new_elements_kind|.
// - Find |target_map|, the newest matching version of this map using the
// virtually "enhanced" |old_map|'s descriptor array (i.e. whose entry at
// |modify_index| is considered to be of |new_kind| and having
// |new_attributes|) to walk the transition tree.
// - Merge/generalize the "enhanced" descriptor array of the |old_map| and
// descriptor array of the |target_map|.
// - Generalize the |modify_index| descriptor using |new_representation| and
// |new_field_type|.
// - Walk the tree again starting from the root towards |target_map|. Stop at
// |split_map|, the first map who's descriptor array does not match the merged
// descriptor array.
// - If |target_map| == |split_map|, |target_map| is in the expected state.
// Return it.
// - Otherwise, invalidate the outdated transition target from |target_map|, and
// replace its transition tree with a new branch for the updated descriptors.
Handle<Map> Map::Reconfigure(Handle<Map> old_map,
ElementsKind new_elements_kind, int modify_index,
PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<FieldType> new_field_type,
StoreMode store_mode) {
DCHECK_NE(kAccessor, new_kind); // TODO(ishell): not supported yet.
DCHECK(store_mode != FORCE_FIELD || modify_index >= 0);
Isolate* isolate = old_map->GetIsolate();
Handle<DescriptorArray> old_descriptors(
old_map->instance_descriptors(), isolate);
int old_nof = old_map->NumberOfOwnDescriptors();
// If it's just a representation generalization case (i.e. property kind and
// attributes stays unchanged) it's fine to transition from None to anything
// but double without any modification to the object, because the default
// uninitialized value for representation None can be overwritten by both
// smi and tagged values. Doubles, however, would require a box allocation.
if (modify_index >= 0 && !new_representation.IsNone() &&
!new_representation.IsDouble() &&
old_map->elements_kind() == new_elements_kind) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
Representation old_representation = old_details.representation();
if (old_representation.IsNone()) {
DCHECK_EQ(new_kind, old_details.kind());
DCHECK_EQ(new_attributes, old_details.attributes());
DCHECK_EQ(DATA, old_details.type());
if (FLAG_trace_generalization) {
old_map->PrintGeneralization(
stdout, "uninitialized field", modify_index,
old_map->NumberOfOwnDescriptors(),
old_map->NumberOfOwnDescriptors(), false, old_representation,
new_representation,
handle(old_descriptors->GetFieldType(modify_index), isolate),
MaybeHandle<Object>(), new_field_type, MaybeHandle<Object>());
}
Handle<Map> field_owner(old_map->FindFieldOwner(modify_index), isolate);
GeneralizeFieldType(field_owner, modify_index, new_representation,
new_field_type);
DCHECK(old_descriptors->GetDetails(modify_index)
.representation()
.Equals(new_representation));
DCHECK(
old_descriptors->GetFieldType(modify_index)->NowIs(new_field_type));
return old_map;
}
}
// Check the state of the root map.
Handle<Map> root_map(old_map->FindRootMap(), isolate);
if (!old_map->EquivalentToForTransition(*root_map)) {
return CopyGeneralizeAllRepresentations(
old_map, new_elements_kind, modify_index, store_mode, new_kind,
new_attributes, "GenAll_NotEquivalent");
}
ElementsKind from_kind = root_map->elements_kind();
ElementsKind to_kind = new_elements_kind;
// TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
if (from_kind != to_kind && to_kind != DICTIONARY_ELEMENTS &&
to_kind != SLOW_STRING_WRAPPER_ELEMENTS &&
to_kind != SLOW_SLOPPY_ARGUMENTS_ELEMENTS &&
!(IsTransitionableFastElementsKind(from_kind) &&
IsMoreGeneralElementsKindTransition(from_kind, to_kind))) {
return CopyGeneralizeAllRepresentations(
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
"GenAll_InvalidElementsTransition");
}
int root_nof = root_map->NumberOfOwnDescriptors();
if (modify_index >= 0 && modify_index < root_nof) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
if (old_details.kind() != new_kind ||
old_details.attributes() != new_attributes) {
return CopyGeneralizeAllRepresentations(
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
"GenAll_RootModification1");
}
if ((old_details.type() != DATA && store_mode == FORCE_FIELD) ||
(old_details.type() == DATA &&
(!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) ||
!new_representation.fits_into(old_details.representation())))) {
return CopyGeneralizeAllRepresentations(
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
"GenAll_RootModification2");
}
}
// From here on, use the map with correct elements kind as root map.
if (from_kind != to_kind) {
root_map = Map::AsElementsKind(root_map, to_kind);
}
Handle<Map> target_map = root_map;
for (int i = root_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyKind next_kind;
PropertyLocation next_location;
PropertyAttributes next_attributes;
Representation next_representation;
bool property_kind_reconfiguration = false;
if (modify_index == i) {
DCHECK_EQ(FORCE_FIELD, store_mode);
property_kind_reconfiguration = old_details.kind() != new_kind;
next_kind = new_kind;
next_location = kField;
next_attributes = new_attributes;
// If property kind is not reconfigured merge the result with
// representation/field type from the old descriptor.
next_representation = new_representation;
if (!property_kind_reconfiguration) {
next_representation =
next_representation.generalize(old_details.representation());
}
} else {
next_kind = old_details.kind();
next_location = old_details.location();
next_attributes = old_details.attributes();
next_representation = old_details.representation();
}
Map* transition = TransitionArray::SearchTransition(
*target_map, next_kind, old_descriptors->GetKey(i), next_attributes);
if (transition == NULL) break;
Handle<Map> tmp_map(transition, isolate);
Handle<DescriptorArray> tmp_descriptors = handle(
tmp_map->instance_descriptors(), isolate);
// Check if target map is incompatible.
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
DCHECK_EQ(next_kind, tmp_details.kind());
DCHECK_EQ(next_attributes, tmp_details.attributes());
if (next_kind == kAccessor &&
!EqualImmutableValues(old_descriptors->GetValue(i),
tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
"GenAll_Incompatible");
}
if (next_location == kField && tmp_details.location() == kDescriptor) break;
Representation tmp_representation = tmp_details.representation();
if (!next_representation.fits_into(tmp_representation)) break;
PropertyLocation old_location = old_details.location();
PropertyLocation tmp_location = tmp_details.location();
if (tmp_location == kField) {
if (next_kind == kData) {
Handle<FieldType> next_field_type;
if (modify_index == i) {
next_field_type = new_field_type;
if (!property_kind_reconfiguration) {
Handle<FieldType> old_field_type =
GetFieldType(isolate, old_descriptors, i,
old_details.location(), tmp_representation);
Representation old_representation = old_details.representation();
next_field_type = GeneralizeFieldType(
old_representation, old_field_type, new_representation,
next_field_type, isolate);
}
} else {
Handle<FieldType> old_field_type =
GetFieldType(isolate, old_descriptors, i, old_details.location(),
tmp_representation);
next_field_type = old_field_type;
}
GeneralizeFieldType(tmp_map, i, tmp_representation, next_field_type);
}
} else if (old_location == kField ||
!EqualImmutableValues(old_descriptors->GetValue(i),
tmp_descriptors->GetValue(i))) {
break;
}
DCHECK(!tmp_map->is_deprecated());
target_map = tmp_map;
}
// Directly change the map if the target map is more general.
Handle<DescriptorArray> target_descriptors(
target_map->instance_descriptors(), isolate);
int target_nof = target_map->NumberOfOwnDescriptors();
if (target_nof == old_nof &&
(store_mode != FORCE_FIELD ||
(modify_index >= 0 &&
target_descriptors->GetDetails(modify_index).location() == kField))) {
#ifdef DEBUG
if (modify_index >= 0) {
PropertyDetails details = target_descriptors->GetDetails(modify_index);
DCHECK_EQ(new_kind, details.kind());
DCHECK_EQ(new_attributes, details.attributes());
DCHECK(new_representation.fits_into(details.representation()));
DCHECK(details.location() != kField ||
new_field_type->NowIs(
target_descriptors->GetFieldType(modify_index)));
}
#endif
if (*target_map != *old_map) {
old_map->NotifyLeafMapLayoutChange();
}
return target_map;
}
// Find the last compatible target map in the transition tree.
for (int i = target_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyKind next_kind;
PropertyAttributes next_attributes;
if (modify_index == i) {
next_kind = new_kind;
next_attributes = new_attributes;
} else {
next_kind = old_details.kind();
next_attributes = old_details.attributes();
}
Map* transition = TransitionArray::SearchTransition(
*target_map, next_kind, old_descriptors->GetKey(i), next_attributes);
if (transition == NULL) break;
Handle<Map> tmp_map(transition, isolate);
Handle<DescriptorArray> tmp_descriptors(
tmp_map->instance_descriptors(), isolate);
// Check if target map is compatible.
#ifdef DEBUG
PropertyDetails tmp_details = tmp_descriptors->GetDetails(i);
DCHECK_EQ(next_kind, tmp_details.kind());
DCHECK_EQ(next_attributes, tmp_details.attributes());
#endif
if (next_kind == kAccessor &&
!EqualImmutableValues(old_descriptors->GetValue(i),
tmp_descriptors->GetValue(i))) {
return CopyGeneralizeAllRepresentations(
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
"GenAll_Incompatible");
}
DCHECK(!tmp_map->is_deprecated());
target_map = tmp_map;
}
target_nof = target_map->NumberOfOwnDescriptors();
target_descriptors = handle(target_map->instance_descriptors(), isolate);
// Allocate a new descriptor array large enough to hold the required
// descriptors, with minimally the exact same size as the old descriptor
// array.
int new_slack = Max(
old_nof, old_descriptors->number_of_descriptors()) - old_nof;
Handle<DescriptorArray> new_descriptors = DescriptorArray::Allocate(
isolate, old_nof, new_slack);
DCHECK(new_descriptors->length() > target_descriptors->length() ||
new_descriptors->NumberOfSlackDescriptors() > 0 ||
new_descriptors->number_of_descriptors() ==
old_descriptors->number_of_descriptors());
DCHECK(new_descriptors->number_of_descriptors() == old_nof);
// 0 -> |root_nof|
int current_offset = 0;
for (int i = 0; i < root_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
if (old_details.location() == kField) {
current_offset += old_details.field_width_in_words();
}
Descriptor d(handle(old_descriptors->GetKey(i), isolate),
handle(old_descriptors->GetValue(i), isolate),
old_details);
new_descriptors->Set(i, &d);
}
// |root_nof| -> |target_nof|
for (int i = root_nof; i < target_nof; ++i) {
Handle<Name> target_key(target_descriptors->GetKey(i), isolate);
PropertyDetails old_details = old_descriptors->GetDetails(i);
PropertyDetails target_details = target_descriptors->GetDetails(i);
PropertyKind next_kind;
PropertyAttributes next_attributes;
PropertyLocation next_location;
Representation next_representation;
bool property_kind_reconfiguration = false;
if (modify_index == i) {
DCHECK_EQ(FORCE_FIELD, store_mode);
property_kind_reconfiguration = old_details.kind() != new_kind;
next_kind = new_kind;
next_attributes = new_attributes;
next_location = kField;
// Merge new representation/field type with ones from the target
// descriptor. If property kind is not reconfigured merge the result with
// representation/field type from the old descriptor.
next_representation =
new_representation.generalize(target_details.representation());
if (!property_kind_reconfiguration) {
next_representation =
next_representation.generalize(old_details.representation());
}
} else {
// Merge old_descriptor and target_descriptor entries.
DCHECK_EQ(target_details.kind(), old_details.kind());
next_kind = target_details.kind();
next_attributes = target_details.attributes();
next_location =
old_details.location() == kField ||
target_details.location() == kField ||
!EqualImmutableValues(target_descriptors->GetValue(i),
old_descriptors->GetValue(i))
? kField
: kDescriptor;
next_representation = old_details.representation().generalize(
target_details.representation());
}
DCHECK_EQ(next_kind, target_details.kind());
DCHECK_EQ(next_attributes, target_details.attributes());
if (next_location == kField) {
if (next_kind == kData) {
Handle<FieldType> target_field_type =
GetFieldType(isolate, target_descriptors, i,
target_details.location(), next_representation);
Handle<FieldType> next_field_type;
if (modify_index == i) {
next_field_type = GeneralizeFieldType(
target_details.representation(), target_field_type,
new_representation, new_field_type, isolate);
if (!property_kind_reconfiguration) {
Handle<FieldType> old_field_type =
GetFieldType(isolate, old_descriptors, i,
old_details.location(), next_representation);
next_field_type = GeneralizeFieldType(
old_details.representation(), old_field_type,
next_representation, next_field_type, isolate);
}
} else {
Handle<FieldType> old_field_type =
GetFieldType(isolate, old_descriptors, i, old_details.location(),
next_representation);
next_field_type = GeneralizeFieldType(
old_details.representation(), old_field_type, next_representation,
target_field_type, isolate);
}
Handle<Object> wrapped_type(WrapType(next_field_type));
Descriptor d =
Descriptor::DataField(target_key, current_offset, wrapped_type,
next_attributes, next_representation);
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
UNIMPLEMENTED(); // TODO(ishell): implement.
}
} else {
PropertyDetails details(next_attributes, next_kind, next_location,
next_representation);
Descriptor d(target_key, handle(target_descriptors->GetValue(i), isolate),
details);
new_descriptors->Set(i, &d);
}
}
// |target_nof| -> |old_nof|
for (int i = target_nof; i < old_nof; ++i) {
PropertyDetails old_details = old_descriptors->GetDetails(i);
Handle<Name> old_key(old_descriptors->GetKey(i), isolate);
// Merge old_descriptor entry and modified details together.
PropertyKind next_kind;
PropertyAttributes next_attributes;
PropertyLocation next_location;
Representation next_representation;
bool property_kind_reconfiguration = false;
if (modify_index == i) {
DCHECK_EQ(FORCE_FIELD, store_mode);
// In case of property kind reconfiguration it is not necessary to
// take into account representation/field type of the old descriptor.
property_kind_reconfiguration = old_details.kind() != new_kind;
next_kind = new_kind;
next_attributes = new_attributes;
next_location = kField;
next_representation = new_representation;
if (!property_kind_reconfiguration) {
next_representation =
next_representation.generalize(old_details.representation());
}
} else {
next_kind = old_details.kind();
next_attributes = old_details.attributes();
next_location = old_details.location();
next_representation = old_details.representation();
}
if (next_location == kField) {
if (next_kind == kData) {
Handle<FieldType> next_field_type;
if (modify_index == i) {
next_field_type = new_field_type;
if (!property_kind_reconfiguration) {
Handle<FieldType> old_field_type =
GetFieldType(isolate, old_descriptors, i,
old_details.location(), next_representation);
next_field_type = GeneralizeFieldType(
old_details.representation(), old_field_type,
next_representation, next_field_type, isolate);
}
} else {
Handle<FieldType> old_field_type =
GetFieldType(isolate, old_descriptors, i, old_details.location(),
next_representation);
next_field_type = old_field_type;
}
Handle<Object> wrapped_type(WrapType(next_field_type));
Descriptor d =
Descriptor::DataField(old_key, current_offset, wrapped_type,
next_attributes, next_representation);
current_offset += d.GetDetails().field_width_in_words();
new_descriptors->Set(i, &d);
} else {
UNIMPLEMENTED(); // TODO(ishell): implement.
}
} else {
PropertyDetails details(next_attributes, next_kind, next_location,
next_representation);
Descriptor d(old_key, handle(old_descriptors->GetValue(i), isolate),
details);
new_descriptors->Set(i, &d);
}
}
new_descriptors->Sort();
DCHECK(store_mode != FORCE_FIELD ||
new_descriptors->GetDetails(modify_index).location() == kField);
Handle<Map> split_map(root_map->FindLastMatchMap(
root_nof, old_nof, *new_descriptors), isolate);
int split_nof = split_map->NumberOfOwnDescriptors();
DCHECK_NE(old_nof, split_nof);
PropertyKind split_kind;
PropertyAttributes split_attributes;
if (modify_index == split_nof) {
split_kind = new_kind;
split_attributes = new_attributes;
} else {
PropertyDetails split_prop_details = old_descriptors->GetDetails(split_nof);
split_kind = split_prop_details.kind();
split_attributes = split_prop_details.attributes();
}
// Invalidate a transition target at |key|.
Map* maybe_transition = TransitionArray::SearchTransition(
*split_map, split_kind, old_descriptors->GetKey(split_nof),
split_attributes);
if (maybe_transition != NULL) {
maybe_transition->DeprecateTransitionTree();
}
// If |maybe_transition| is not NULL then the transition array already
// contains entry for given descriptor. This means that the transition
// could be inserted regardless of whether transitions array is full or not.
if (maybe_transition == NULL &&
!TransitionArray::CanHaveMoreTransitions(split_map)) {
return CopyGeneralizeAllRepresentations(
old_map, to_kind, modify_index, store_mode, new_kind, new_attributes,
"GenAll_CantHaveMoreTransitions");
}
old_map->NotifyLeafMapLayoutChange();
if (FLAG_trace_generalization && modify_index >= 0) {
PropertyDetails old_details = old_descriptors->GetDetails(modify_index);
PropertyDetails new_details = new_descriptors->GetDetails(modify_index);
MaybeHandle<FieldType> old_field_type;
MaybeHandle<FieldType> new_field_type;
MaybeHandle<Object> old_value;
MaybeHandle<Object> new_value;
if (old_details.type() == DATA) {
old_field_type =
handle(old_descriptors->GetFieldType(modify_index), isolate);
} else {
old_value = handle(old_descriptors->GetValue(modify_index), isolate);
}
if (new_details.type() == DATA) {
new_field_type =
handle(new_descriptors->GetFieldType(modify_index), isolate);
} else {
new_value = handle(new_descriptors->GetValue(modify_index), isolate);
}
old_map->PrintGeneralization(
stdout, "", modify_index, split_nof, old_nof,
old_details.location() == kDescriptor && store_mode == FORCE_FIELD,
old_details.representation(), new_details.representation(),
old_field_type, old_value, new_field_type, new_value);
}
Handle<LayoutDescriptor> new_layout_descriptor =
LayoutDescriptor::New(split_map, new_descriptors, old_nof);
Handle<Map> new_map =
AddMissingTransitions(split_map, new_descriptors, new_layout_descriptor);
// Deprecated part of the transition tree is no longer reachable, so replace
// current instance descriptors in the "survived" part of the tree with
// the new descriptors to maintain descriptors sharing invariant.
split_map->ReplaceDescriptors(*new_descriptors, *new_layout_descriptor);
return new_map;
// TODO(ishell): remove.
// static
Handle<Map> Map::ReconfigureElementsKind(Handle<Map> map,
ElementsKind new_elements_kind) {
MapUpdater mu(map->GetIsolate(), map);
return mu.ReconfigureElementsKind(new_elements_kind);
}
// Generalize the representation of all DATA descriptors.
Handle<Map> Map::GeneralizeAllFieldRepresentations(
Handle<Map> map) {
Isolate* isolate = map->GetIsolate();
Handle<FieldType> any_type = FieldType::Any(isolate);
Handle<DescriptorArray> descriptors(map->instance_descriptors());
for (int i = 0; i < map->NumberOfOwnDescriptors(); ++i) {
PropertyDetails details = descriptors->GetDetails(i);
if (details.type() == DATA) {
map = ReconfigureProperty(map, i, kData, details.attributes(),
Representation::Tagged(),
FieldType::Any(map->GetIsolate()), FORCE_FIELD);
MapUpdater mu(isolate, map);
map = mu.ReconfigureToDataField(i, details.attributes(),
Representation::Tagged(), any_type);
}
}
return map;
@ -4869,9 +4284,8 @@ Map* Map::TryReplayPropertyTransitions(Map* old_map) {
// static
Handle<Map> Map::Update(Handle<Map> map) {
if (!map->is_deprecated()) return map;
return ReconfigureProperty(map, -1, kData, NONE, Representation::None(),
FieldType::None(map->GetIsolate()),
ALLOW_IN_DESCRIPTOR);
MapUpdater mu(map->GetIsolate(), map);
return mu.Update();
}
Maybe<bool> JSObject::SetPropertyWithInterceptor(LookupIterator* it,
@ -9718,8 +9132,9 @@ Handle<Map> UpdateDescriptorForValue(Handle<Map> map, int descriptor,
Representation representation = value->OptimalRepresentation();
Handle<FieldType> type = value->OptimalType(isolate, representation);
return Map::ReconfigureProperty(map, descriptor, kData, attributes,
representation, type, FORCE_FIELD);
MapUpdater mu(isolate, map);
return mu.ReconfigureToDataField(descriptor, attributes, representation,
type);
}
} // namespace
@ -9812,9 +9227,11 @@ Handle<Map> Map::ReconfigureExistingProperty(Handle<Map> map, int descriptor,
}
Isolate* isolate = map->GetIsolate();
Handle<Map> new_map = ReconfigureProperty(
map, descriptor, kind, attributes, Representation::None(),
FieldType::None(isolate), FORCE_FIELD);
MapUpdater mu(isolate, map);
DCHECK_EQ(kData, kind); // Only kData case is supported so far.
Handle<Map> new_map = mu.ReconfigureToDataField(
descriptor, attributes, Representation::None(), FieldType::None(isolate));
return new_map;
}

View File

@ -6059,17 +6059,19 @@ class Map: public HeapObject {
MUST_USE_RESULT static Handle<FieldType> GeneralizeFieldType(
Representation rep1, Handle<FieldType> type1, Representation rep2,
Handle<FieldType> type2, Isolate* isolate);
static void GeneralizeFieldType(Handle<Map> map, int modify_index,
Representation new_representation,
Handle<FieldType> new_field_type);
static void GeneralizeField(Handle<Map> map, int modify_index,
Representation new_representation,
Handle<FieldType> new_field_type);
static inline Handle<Map> ReconfigureProperty(
Handle<Map> map, int modify_index, PropertyKind new_kind,
PropertyAttributes new_attributes, Representation new_representation,
Handle<FieldType> new_field_type, StoreMode store_mode);
static Handle<Map> ReconfigureProperty(Handle<Map> map, int modify_index,
PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<FieldType> new_field_type,
StoreMode store_mode);
static inline Handle<Map> ReconfigureElementsKind(
Handle<Map> map, ElementsKind new_elements_kind);
static Handle<Map> ReconfigureElementsKind(Handle<Map> map,
ElementsKind new_elements_kind);
static Handle<Map> PrepareForDataProperty(Handle<Map> old_map,
int descriptor_number,
@ -6192,6 +6194,8 @@ class Map: public HeapObject {
Descriptor* descriptor,
TransitionFlag flag);
static Handle<Object> WrapType(Handle<FieldType> type);
MUST_USE_RESULT static MaybeHandle<Map> CopyWithField(
Handle<Map> map, Handle<Name> name, Handle<FieldType> type,
PropertyAttributes attributes, Representation representation,
@ -6504,14 +6508,7 @@ class Map: public HeapObject {
static Handle<Map> CopyNormalized(Handle<Map> map,
PropertyNormalizationMode mode);
static Handle<Map> Reconfigure(Handle<Map> map,
ElementsKind new_elements_kind,
int modify_index, PropertyKind new_kind,
PropertyAttributes new_attributes,
Representation new_representation,
Handle<FieldType> new_field_type,
StoreMode store_mode);
// TODO(ishell): Move to MapUpdater.
static Handle<Map> CopyGeneralizeAllRepresentations(
Handle<Map> map, ElementsKind elements_kind, int modify_index,
StoreMode store_mode, PropertyKind kind, PropertyAttributes attributes,
@ -6528,8 +6525,6 @@ class Map: public HeapObject {
LayoutDescriptor* new_layout_descriptor);
Map* FindLastMatchMap(int verbatim, int length, DescriptorArray* descriptors);
// Update field type of the given descriptor to new representation and new
// type. The type must be prepared for storing in descriptor array:
// it must be either a simple type or a map wrapped in a weak cell.
@ -6537,8 +6532,10 @@ class Map: public HeapObject {
Representation new_representation,
Handle<Object> new_wrapped_type);
// TODO(ishell): Move to MapUpdater.
void PrintReconfiguration(FILE* file, int modify_index, PropertyKind kind,
PropertyAttributes attributes);
// TODO(ishell): Move to MapUpdater.
void PrintGeneralization(FILE* file, const char* reason, int modify_index,
int split, int descriptors, bool constant_to_field,
Representation old_representation,
@ -6547,10 +6544,11 @@ class Map: public HeapObject {
MaybeHandle<Object> old_value,
MaybeHandle<FieldType> new_field_type,
MaybeHandle<Object> new_value);
static const int kFastPropertiesSoftLimit = 12;
static const int kMaxFastProperties = 128;
friend class MapUpdater;
DISALLOW_IMPLICIT_CONSTRUCTORS(Map);
};

View File

@ -254,7 +254,7 @@ class PropertyDetails BASE_EMBEDDED {
| FieldIndexField::encode(field_index);
}
PropertyDetails(PropertyAttributes attributes, PropertyKind kind,
PropertyDetails(PropertyKind kind, PropertyAttributes attributes,
PropertyLocation location, Representation representation,
int field_index = 0) {
value_ = KindField::encode(kind) | LocationField::encode(location) |

View File

@ -85,6 +85,7 @@ class Descriptor final BASE_EMBEDDED {
friend class DescriptorArray;
friend class Map;
friend class MapUpdater;
};
std::ostream& operator<<(std::ostream& os, const Descriptor& d);

View File

@ -1060,6 +1060,8 @@
'lookup-cache.h',
'lookup.cc',
'lookup.h',
'map-updater.cc',
'map-updater.h',
'macro-assembler.h',
'machine-type.cc',
'machine-type.h',

View File

@ -1664,8 +1664,8 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
->NowContains(value)) {
Handle<FieldType> value_type =
value->OptimalType(isolate_, expected_representation);
Map::GeneralizeFieldType(target, descriptor,
expected_representation, value_type);
Map::GeneralizeField(target, descriptor, expected_representation,
value_type);
}
DCHECK(target->instance_descriptors()
->GetFieldType(descriptor)