Chromium Code Reviews| Index: src/map-reconfigurer.cc |
| diff --git a/src/map-reconfigurer.cc b/src/map-reconfigurer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b79255ad07ccfe7d61426319a1044908067784b5 |
| --- /dev/null |
| +++ b/src/map-reconfigurer.cc |
| @@ -0,0 +1,617 @@ |
| +// 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-reconfigurer.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 |
| + |
| +Handle<Map> MapReconfigurer::Result() const { |
| + DCHECK_EQ(kEnd, state_); |
| + return result_map_; |
| +} |
| + |
| +Name* MapReconfigurer::GetKey(int descriptor) const { |
| + return old_descriptors_->GetKey(descriptor); |
| +} |
| + |
| +PropertyDetails MapReconfigurer::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* MapReconfigurer::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* MapReconfigurer::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> MapReconfigurer::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> MapReconfigurer::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> MapReconfigurer::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::GeneralizeFieldTypes( |
| + old_representation, old_field_type, new_representation_, |
| + new_field_type_, isolate_); |
| + } |
| + |
| + if (TryRecofigureToDataFieldInplace()) return Result(); |
| + if (FindRootMap()) return Result(); |
|
Toon Verwaest
2017/01/09 13:01:19
This is odd to read... I'd expect if (!FindRootMap
Igor Sheludko
2017/01/09 14:54:25
true means "done", therefore it returns. And all t
Igor Sheludko
2017/01/10 10:28:08
As discussed offline, these methods now return an
|
| + if (FindTargetMap()) return Result(); |
| + ConstructNewMap(); |
| + return Result(); |
| +} |
| + |
| +Handle<Map> MapReconfigurer::ReconfigureElementsKind( |
| + ElementsKind elements_kind) { |
| + DCHECK_EQ(kInitialized, state_); |
| + new_elements_kind_ = elements_kind; |
| + |
| + if (FindRootMap()) return Result(); |
| + if (FindTargetMap()) return Result(); |
| + ConstructNewMap(); |
| + return Result(); |
| +} |
| + |
| +Handle<Map> MapReconfigurer::Update() { |
| + DCHECK_EQ(kInitialized, state_); |
| + DCHECK(old_map_->is_deprecated()); |
| + |
| + if (FindRootMap()) return Result(); |
| + if (FindTargetMap()) return Result(); |
| + ConstructNewMap(); |
| + return Result(); |
| +} |
| + |
| +bool MapReconfigurer::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; |
|
Toon Verwaest
2017/01/09 13:01:19
these states are a little odd. That's just for deb
Igor Sheludko
2017/01/09 14:54:25
Right. And the updater instance should not be used
|
| + return true; // Done. |
| +} |
| + |
| +bool MapReconfigurer::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 false; // Not done yet. |
| + } |
| + |
| + PropertyDetails old_details = |
| + old_descriptors_->GetDetails(modified_descriptor_); |
| + Representation old_representation = old_details.representation(); |
| + if (!old_representation.IsNone()) { |
| + return false; // 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::GeneralizeFieldType(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 true; // Done. |
| +} |
| + |
| +bool MapReconfigurer::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 false; // Not done yet. |
| +} |
| + |
| +bool MapReconfigurer::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::GeneralizeFieldType(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 true; // 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 false; // Not done yet. |
| +} |
| + |
| +Handle<DescriptorArray> MapReconfigurer::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 virtually modified 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::GeneralizeFieldTypes( |
| + 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 virtually modified 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> MapReconfigurer::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_); |
| +} |
| + |
| +bool MapReconfigurer::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 true; // Done. |
| +} |
| + |
| +} // namespace internal |
| +} // namespace v8 |