summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects/map-updater.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/objects/map-updater.cc')
-rw-r--r--deps/v8/src/objects/map-updater.cc805
1 files changed, 805 insertions, 0 deletions
diff --git a/deps/v8/src/objects/map-updater.cc b/deps/v8/src/objects/map-updater.cc
new file mode 100644
index 0000000000..855fdabdf3
--- /dev/null
+++ b/deps/v8/src/objects/map-updater.cc
@@ -0,0 +1,805 @@
+// 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/objects/map-updater.h"
+
+#include "src/execution/isolate.h"
+#include "src/handles/handles.h"
+#include "src/objects/field-type.h"
+#include "src/objects/objects-inl.h"
+#include "src/objects/objects.h"
+#include "src/objects/property-details.h"
+#include "src/objects/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
+
+MapUpdater::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()),
+ is_transitionable_fast_elements_kind_(
+ IsTransitionableFastElementsKind(new_elements_kind_)) {
+ // We shouldn't try to update remote objects.
+ DCHECK(
+ !old_map->FindRootMap(isolate).GetConstructor().IsFunctionTemplateInfo());
+}
+
+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_) {
+ PropertyAttributes attributes = new_attributes_;
+ // If the original map was sealed or frozen, let us used the old
+ // attributes so that we follow the same transition path as before.
+ // Note that the user could not have changed the attributes because
+ // both seal and freeze make the properties non-configurable.
+ if (integrity_level_ == SEALED || integrity_level_ == FROZEN) {
+ attributes = old_descriptors_->GetDetails(descriptor).attributes();
+ }
+ return PropertyDetails(new_kind_, attributes, new_location_, new_constness_,
+ 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_->GetStrongValue(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->GetStrongValue(descriptor)
+ .OptimalType(isolate_, representation);
+ }
+}
+
+Handle<Map> MapUpdater::ReconfigureToDataField(int descriptor,
+ PropertyAttributes attributes,
+ PropertyConstness constness,
+ 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;
+
+ 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_) {
+ new_constness_ = GeneralizeConstness(constness, old_details.constness());
+
+ Representation old_representation = old_details.representation();
+ new_representation_ = 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_, field_type, isolate_);
+ } else {
+ // We don't know if this is a first property kind reconfiguration
+ // and we don't know which value was in this property previously
+ // therefore we can't treat such a property as constant.
+ new_constness_ = PropertyConstness::kMutable;
+ new_representation_ = representation;
+ new_field_type_ = field_type;
+ }
+
+ Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
+ isolate_, old_map_->instance_type(), &new_representation_,
+ &new_field_type_);
+
+ if (TryReconfigureToDataFieldInplace() == kEnd) return result_map_;
+ if (FindRootMap() == kEnd) return result_map_;
+ if (FindTargetMap() == kEnd) return result_map_;
+ if (ConstructNewMap() == kAtIntegrityLevelSource) {
+ ConstructNewMapWithIntegrityLevelTransition();
+ }
+ DCHECK_EQ(kEnd, state_);
+ return result_map_;
+}
+
+Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) {
+ DCHECK_EQ(kInitialized, state_);
+ new_elements_kind_ = elements_kind;
+ is_transitionable_fast_elements_kind_ =
+ IsTransitionableFastElementsKind(new_elements_kind_);
+
+ if (FindRootMap() == kEnd) return result_map_;
+ if (FindTargetMap() == kEnd) return result_map_;
+ if (ConstructNewMap() == kAtIntegrityLevelSource) {
+ ConstructNewMapWithIntegrityLevelTransition();
+ }
+ 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_;
+ if (ConstructNewMap() == kAtIntegrityLevelSource) {
+ ConstructNewMapWithIntegrityLevelTransition();
+ }
+ DCHECK_EQ(kEnd, state_);
+ if (FLAG_fast_map_update) {
+ TransitionsAccessor(isolate_, old_map_).SetMigrationTarget(*result_map_);
+ }
+ return result_map_;
+}
+
+void MapUpdater::GeneralizeField(Handle<Map> map, int modify_index,
+ PropertyConstness new_constness,
+ Representation new_representation,
+ Handle<FieldType> new_field_type) {
+ Map::GeneralizeField(isolate_, map, modify_index, new_constness,
+ new_representation, new_field_type);
+
+ DCHECK(*old_descriptors_ == old_map_->instance_descriptors() ||
+ *old_descriptors_ == integrity_source_map_->instance_descriptors());
+}
+
+MapUpdater::State MapUpdater::CopyGeneralizeAllFields(const char* reason) {
+ result_map_ = Map::CopyGeneralizeAllFields(
+ isolate_, old_map_, new_elements_kind_, modified_descriptor_, new_kind_,
+ new_attributes_, reason);
+ state_ = kEnd;
+ return state_; // Done.
+}
+
+MapUpdater::State MapUpdater::TryReconfigureToDataFieldInplace() {
+ // Updating deprecated maps in-place doesn't make sense.
+ if (old_map_->is_deprecated()) return state_;
+
+ if (new_representation_.IsNone()) return state_; // Not done yet.
+
+ PropertyDetails old_details =
+ old_descriptors_->GetDetails(modified_descriptor_);
+ Representation old_representation = old_details.representation();
+ if (!old_representation.CanBeInPlaceChangedTo(new_representation_)) {
+ 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(
+ isolate_, stdout, "uninitialized field", modified_descriptor_, old_nof_,
+ old_nof_, false, old_representation, new_representation_,
+ old_details.constness(), new_constness_,
+ handle(old_descriptors_->GetFieldType(modified_descriptor_), isolate_),
+ MaybeHandle<Object>(), new_field_type_, MaybeHandle<Object>());
+ }
+ Handle<Map> field_owner(
+ old_map_->FindFieldOwner(isolate_, modified_descriptor_), isolate_);
+
+ GeneralizeField(field_owner, modified_descriptor_, new_constness_,
+ 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.
+}
+
+bool MapUpdater::TrySaveIntegrityLevelTransitions() {
+ // Figure out the most restrictive integrity level transition (it should
+ // be the last one in the transition tree).
+ Handle<Map> previous =
+ handle(Map::cast(old_map_->GetBackPointer()), isolate_);
+ Symbol integrity_level_symbol;
+ TransitionsAccessor last_transitions(isolate_, previous);
+ if (!last_transitions.HasIntegrityLevelTransitionTo(
+ *old_map_, &integrity_level_symbol, &integrity_level_)) {
+ // The last transition was not integrity level transition - just bail out.
+ // This can happen in the following cases:
+ // - there are private symbol transitions following the integrity level
+ // transitions (see crbug.com/v8/8854).
+ // - there is a getter added in addition to an existing setter (or a setter
+ // in addition to an existing getter).
+ return false;
+ }
+ integrity_level_symbol_ = handle(integrity_level_symbol, isolate_);
+ integrity_source_map_ = previous;
+
+ // Now walk up the back pointer chain and skip all integrity level
+ // transitions. If we encounter any non-integrity level transition interleaved
+ // with integrity level transitions, just bail out.
+ while (!integrity_source_map_->is_extensible()) {
+ previous =
+ handle(Map::cast(integrity_source_map_->GetBackPointer()), isolate_);
+ TransitionsAccessor transitions(isolate_, previous);
+ if (!transitions.HasIntegrityLevelTransitionTo(*integrity_source_map_)) {
+ return false;
+ }
+ integrity_source_map_ = previous;
+ }
+
+ // Integrity-level transitions never change number of descriptors.
+ CHECK_EQ(old_map_->NumberOfOwnDescriptors(),
+ integrity_source_map_->NumberOfOwnDescriptors());
+
+ has_integrity_level_transition_ = true;
+ old_descriptors_ =
+ handle(integrity_source_map_->instance_descriptors(), isolate_);
+ return true;
+}
+
+MapUpdater::State MapUpdater::FindRootMap() {
+ DCHECK_EQ(kInitialized, state_);
+ // Check the state of the root map.
+ root_map_ = handle(old_map_->FindRootMap(isolate_), isolate_);
+ ElementsKind from_kind = root_map_->elements_kind();
+ ElementsKind to_kind = new_elements_kind_;
+
+ if (root_map_->is_deprecated()) {
+ state_ = kEnd;
+ result_map_ = handle(
+ JSFunction::cast(root_map_->GetConstructor()).initial_map(), isolate_);
+ result_map_ = Map::AsElementsKind(isolate_, result_map_, to_kind);
+ DCHECK(result_map_->is_dictionary_map());
+ return state_;
+ }
+
+ if (!old_map_->EquivalentToForTransition(*root_map_)) {
+ return CopyGeneralizeAllFields("GenAll_NotEquivalent");
+ } else if (old_map_->is_extensible() != root_map_->is_extensible()) {
+ DCHECK(!old_map_->is_extensible());
+ DCHECK(root_map_->is_extensible());
+ // We have an integrity level transition in the tree, let us make a note
+ // of that transition to be able to replay it later.
+ if (!TrySaveIntegrityLevelTransitions()) {
+ return CopyGeneralizeAllFields("GenAll_PrivateSymbolsOnNonExtensible");
+ }
+
+ // We want to build transitions to the original element kind (before
+ // the seal transitions), so change {to_kind} accordingly.
+ DCHECK(to_kind == DICTIONARY_ELEMENTS ||
+ to_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
+ IsTypedArrayElementsKind(to_kind) ||
+ IsFrozenOrSealedElementsKind(to_kind));
+ to_kind = integrity_source_map_->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 CopyGeneralizeAllFields("GenAll_InvalidElementsTransition");
+ }
+
+ int root_nof = root_map_->NumberOfOwnDescriptors();
+ 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 CopyGeneralizeAllFields("GenAll_RootModification1");
+ }
+ if (old_details.location() != kField) {
+ return CopyGeneralizeAllFields("GenAll_RootModification2");
+ }
+ if (!new_representation_.fits_into(old_details.representation())) {
+ return CopyGeneralizeAllFields("GenAll_RootModification4");
+ }
+
+ DCHECK_EQ(kData, old_details.kind());
+ DCHECK_EQ(kData, new_kind_);
+ DCHECK_EQ(kField, new_location_);
+
+ // Modify root map in-place. The GeneralizeField method is a no-op
+ // if the {old_map_} is already general enough to hold the requested
+ // {new_constness_} and {new_field_type_}.
+ GeneralizeField(old_map_, modified_descriptor_, new_constness_,
+ old_details.representation(), new_field_type_);
+ }
+
+ // From here on, use the map with correct elements kind as root map.
+ root_map_ = Map::AsElementsKind(isolate_, 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 = TransitionsAccessor(isolate_, target_map_)
+ .SearchTransition(GetKey(i), old_details.kind(),
+ old_details.attributes());
+ if (transition.is_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->GetStrongValue(i))) {
+ // TODO(ishell): mutable accessors are not implemented yet.
+ return CopyGeneralizeAllFields("GenAll_Incompatible");
+ }
+ if (!IsGeneralizableTo(old_details.location(), tmp_details.location())) {
+ break;
+ }
+ 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);
+ GeneralizeField(tmp_map, i, old_details.constness(), tmp_representation,
+ old_field_type);
+ } else {
+ // kDescriptor: Check that the value matches.
+ if (!EqualImmutableValues(GetValue(i),
+ tmp_descriptors->GetStrongValue(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(GetDetails(modified_descriptor_).attributes(),
+ details.attributes());
+ DCHECK(IsGeneralizableTo(new_constness_, details.constness()));
+ 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.GetStrongValue(modified_descriptor_)));
+ }
+ }
+#endif
+ if (*target_map_ != *old_map_) {
+ old_map_->NotifyLeafMapLayoutChange(isolate_);
+ }
+ if (!has_integrity_level_transition_) {
+ result_map_ = target_map_;
+ state_ = kEnd;
+ return state_; // Done.
+ }
+
+ // We try to replay the integrity level transition here.
+ Map transition = TransitionsAccessor(isolate_, target_map_)
+ .SearchSpecial(*integrity_level_symbol_);
+ if (!transition.is_null()) {
+ result_map_ = handle(transition, isolate_);
+ 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 = TransitionsAccessor(isolate_, target_map_)
+ .SearchTransition(GetKey(i), old_details.kind(),
+ old_details.attributes());
+ if (transition.is_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->GetStrongValue(i))) {
+ return CopyGeneralizeAllFields("GenAll_Incompatible");
+ }
+ DCHECK(!tmp_map->is_deprecated());
+ target_map_ = tmp_map;
+ }
+
+ state_ = kAtTargetMap;
+ return state_; // Not done yet.
+}
+
+Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() {
+ InstanceType instance_type = old_map_->instance_type();
+ 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 =
+ std::max<int>(old_nof_, old_descriptors_->number_of_descriptors()) -
+ old_nof_;
+ Handle<DescriptorArray> new_descriptors =
+ DescriptorArray::Allocate(isolate_, old_nof_, new_slack);
+ DCHECK(new_descriptors->number_of_all_descriptors() >
+ target_descriptors->number_of_all_descriptors() ||
+ new_descriptors->number_of_slack_descriptors() > 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_),
+ MaybeObjectHandle(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();
+ DCHECK_EQ(next_kind, target_details.kind());
+ DCHECK_EQ(next_attributes, target_details.attributes());
+
+ PropertyConstness next_constness = GeneralizeConstness(
+ old_details.constness(), target_details.constness());
+
+ // Note: failed values equality check does not invalidate per-object
+ // property constness.
+ PropertyLocation next_location =
+ old_details.location() == kField ||
+ target_details.location() == kField ||
+ !EqualImmutableValues(target_descriptors->GetStrongValue(i),
+ GetValue(i))
+ ? kField
+ : kDescriptor;
+
+ // Ensure that mutable values are stored in fields.
+ DCHECK_IMPLIES(next_constness == PropertyConstness::kMutable,
+ next_location == kField);
+
+ Representation next_representation =
+ old_details.representation().generalize(
+ target_details.representation());
+
+ 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_);
+
+ Map::GeneralizeIfCanHaveTransitionableFastElementsKind(
+ isolate_, instance_type, &next_representation, &next_field_type);
+
+ MaybeObjectHandle wrapped_type(
+ Map::WrapFieldType(isolate_, next_field_type));
+ Descriptor d;
+ if (next_kind == kData) {
+ d = Descriptor::DataField(key, current_offset, next_attributes,
+ next_constness, next_representation,
+ wrapped_type);
+ } 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);
+ DCHECK_EQ(PropertyConstness::kConst, next_constness);
+
+ Handle<Object> value(GetValue(i), isolate_);
+ DCHECK_EQ(kAccessor, next_kind);
+ Descriptor 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();
+ PropertyConstness next_constness = old_details.constness();
+ PropertyLocation next_location = old_details.location();
+ Representation next_representation = old_details.representation();
+
+ Descriptor d;
+ if (next_location == kField) {
+ Handle<FieldType> next_field_type =
+ GetOrComputeFieldType(i, old_details.location(), next_representation);
+
+ // If the |new_elements_kind_| is still transitionable then the old map's
+ // elements kind is also transitionable and therefore the old descriptors
+ // array must already have generalized field type.
+ CHECK_IMPLIES(
+ is_transitionable_fast_elements_kind_,
+ Map::IsMostGeneralFieldType(next_representation, *next_field_type));
+
+ MaybeObjectHandle wrapped_type(
+ Map::WrapFieldType(isolate_, next_field_type));
+ Descriptor d;
+ if (next_kind == kData) {
+ d = Descriptor::DataField(key, current_offset, next_attributes,
+ next_constness, next_representation,
+ wrapped_type);
+ } 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);
+ DCHECK_EQ(PropertyConstness::kConst, next_constness);
+
+ 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 =
+ TransitionsAccessor(isolate_, current, &no_allocation)
+ .SearchTransition(name, details.kind(), details.attributes());
+ if (next.is_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.constness() != next_details.constness()) break;
+ 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->GetStrongValue(i),
+ next_descriptors.GetStrongValue(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();
+ if (old_nof_ == split_nof) {
+ CHECK(has_integrity_level_transition_);
+ state_ = kAtIntegrityLevelSource;
+ return state_;
+ }
+
+ PropertyDetails split_details = GetDetails(split_nof);
+ TransitionsAccessor transitions(isolate_, split_map);
+
+ // Invalidate a transition target at |key|.
+ Map maybe_transition = transitions.SearchTransition(
+ GetKey(split_nof), split_details.kind(), split_details.attributes());
+ if (!maybe_transition.is_null()) {
+ maybe_transition.DeprecateTransitionTree(isolate_);
+ }
+
+ // If |maybe_transition| is not nullptr 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.is_null() && !transitions.CanHaveMoreTransitions()) {
+ return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
+ }
+
+ old_map_->NotifyLeafMapLayoutChange(isolate_);
+
+ 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.location() == kField) {
+ old_field_type = handle(
+ old_descriptors_->GetFieldType(modified_descriptor_), isolate_);
+ } else {
+ old_value = handle(old_descriptors_->GetStrongValue(modified_descriptor_),
+ isolate_);
+ }
+ if (new_details.location() == kField) {
+ new_field_type =
+ handle(new_descriptors->GetFieldType(modified_descriptor_), isolate_);
+ } else {
+ new_value = handle(new_descriptors->GetStrongValue(modified_descriptor_),
+ isolate_);
+ }
+
+ old_map_->PrintGeneralization(
+ isolate_, stdout, "", modified_descriptor_, split_nof, old_nof_,
+ old_details.location() == kDescriptor && new_location_ == kField,
+ old_details.representation(), new_details.representation(),
+ old_details.constness(), new_details.constness(), old_field_type,
+ old_value, new_field_type, new_value);
+ }
+
+ Handle<LayoutDescriptor> new_layout_descriptor =
+ LayoutDescriptor::New(isolate_, split_map, new_descriptors, old_nof_);
+
+ Handle<Map> new_map = Map::AddMissingTransitions(
+ isolate_, 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(isolate_, *new_descriptors,
+ *new_layout_descriptor);
+
+ if (has_integrity_level_transition_) {
+ target_map_ = new_map;
+ state_ = kAtIntegrityLevelSource;
+ } else {
+ result_map_ = new_map;
+ state_ = kEnd;
+ }
+ return state_; // Done.
+}
+
+MapUpdater::State MapUpdater::ConstructNewMapWithIntegrityLevelTransition() {
+ DCHECK_EQ(kAtIntegrityLevelSource, state_);
+
+ TransitionsAccessor transitions(isolate_, target_map_);
+ if (!transitions.CanHaveMoreTransitions()) {
+ return CopyGeneralizeAllFields("GenAll_CantHaveMoreTransitions");
+ }
+
+ result_map_ = Map::CopyForPreventExtensions(
+ isolate_, target_map_, integrity_level_, integrity_level_symbol_,
+ "CopyForPreventExtensions",
+ old_map_->elements_kind() == DICTIONARY_ELEMENTS);
+ DCHECK_IMPLIES(old_map_->elements_kind() == DICTIONARY_ELEMENTS,
+ result_map_->elements_kind() == DICTIONARY_ELEMENTS);
+
+ state_ = kEnd;
+ return state_;
+}
+
+} // namespace internal
+} // namespace v8