diff options
Diffstat (limited to 'deps/v8/src/map-updater.cc')
-rw-r--r-- | deps/v8/src/map-updater.cc | 161 |
1 files changed, 134 insertions, 27 deletions
diff --git a/deps/v8/src/map-updater.cc b/deps/v8/src/map-updater.cc index 6825f1ac83..c6209cd9d6 100644 --- a/deps/v8/src/map-updater.cc +++ b/deps/v8/src/map-updater.cc @@ -9,6 +9,7 @@ #include "src/isolate.h" #include "src/objects-inl.h" #include "src/objects.h" +#include "src/property-details.h" #include "src/transitions.h" namespace v8 { @@ -45,8 +46,16 @@ Name MapUpdater::GetKey(int descriptor) const { PropertyDetails MapUpdater::GetDetails(int descriptor) const { DCHECK_LE(0, descriptor); if (descriptor == modified_descriptor_) { - return PropertyDetails(new_kind_, new_attributes_, new_location_, - new_constness_, new_representation_); + 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); } @@ -141,10 +150,12 @@ Handle<Map> MapUpdater::ReconfigureToDataField(int descriptor, isolate_, old_map_->instance_type(), &new_constness_, &new_representation_, &new_field_type_); - if (TryRecofigureToDataFieldInplace() == kEnd) return result_map_; + if (TryReconfigureToDataFieldInplace() == kEnd) return result_map_; if (FindRootMap() == kEnd) return result_map_; if (FindTargetMap() == kEnd) return result_map_; - ConstructNewMap(); + if (ConstructNewMap() == kAtIntegrityLevelSource) { + ConstructNewMapWithIntegrityLevelTransition(); + } DCHECK_EQ(kEnd, state_); return result_map_; } @@ -157,7 +168,9 @@ Handle<Map> MapUpdater::ReconfigureElementsKind(ElementsKind elements_kind) { if (FindRootMap() == kEnd) return result_map_; if (FindTargetMap() == kEnd) return result_map_; - ConstructNewMap(); + if (ConstructNewMap() == kAtIntegrityLevelSource) { + ConstructNewMapWithIntegrityLevelTransition(); + } DCHECK_EQ(kEnd, state_); return result_map_; } @@ -168,7 +181,9 @@ Handle<Map> MapUpdater::Update() { if (FindRootMap() == kEnd) return result_map_; if (FindTargetMap() == kEnd) return result_map_; - ConstructNewMap(); + if (ConstructNewMap() == kAtIntegrityLevelSource) { + ConstructNewMapWithIntegrityLevelTransition(); + } DCHECK_EQ(kEnd, state_); if (FLAG_fast_map_update) { TransitionsAccessor(isolate_, old_map_).SetMigrationTarget(*result_map_); @@ -183,7 +198,8 @@ void MapUpdater::GeneralizeField(Handle<Map> map, int modify_index, Map::GeneralizeField(isolate_, map, modify_index, new_constness, new_representation, new_field_type); - DCHECK_EQ(*old_descriptors_, old_map_->instance_descriptors()); + DCHECK(*old_descriptors_ == old_map_->instance_descriptors() || + *old_descriptors_ == integrity_source_map_->instance_descriptors()); } MapUpdater::State MapUpdater::CopyGeneralizeAllFields(const char* reason) { @@ -194,7 +210,7 @@ MapUpdater::State MapUpdater::CopyGeneralizeAllFields(const char* reason) { return state_; // Done. } -MapUpdater::State MapUpdater::TryRecofigureToDataFieldInplace() { +MapUpdater::State MapUpdater::TryReconfigureToDataFieldInplace() { // 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 @@ -238,12 +254,56 @@ MapUpdater::State MapUpdater::TryRecofigureToDataFieldInplace() { 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( @@ -252,9 +312,24 @@ MapUpdater::State MapUpdater::FindRootMap() { DCHECK(result_map_->is_dictionary_map()); return state_; } - int root_nof = root_map_->NumberOfOwnDescriptors(); + 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 || + IsFixedTypedArrayElementsKind(to_kind)); + to_kind = integrity_source_map_->elements_kind(); } // TODO(ishell): Add a test for SLOW_SLOPPY_ARGUMENTS_ELEMENTS. @@ -266,6 +341,7 @@ MapUpdater::State MapUpdater::FindRootMap() { 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_); @@ -276,8 +352,7 @@ MapUpdater::State MapUpdater::FindRootMap() { if (old_details.location() != kField) { return CopyGeneralizeAllFields("GenAll_RootModification2"); } - if (new_constness_ != old_details.constness() && - (!FLAG_modify_map_inplace || !old_map_->is_prototype_map())) { + if (new_constness_ != old_details.constness() && !FLAG_modify_map_inplace) { return CopyGeneralizeAllFields("GenAll_RootModification3"); } if (!new_representation_.fits_into(old_details.representation())) { @@ -295,11 +370,6 @@ MapUpdater::State MapUpdater::FindRootMap() { // Modify root map in-place. if (FLAG_modify_map_inplace && new_constness_ != old_details.constness()) { - // Only prototype root maps are allowed to be updated in-place. - // TODO(ishell): fix all the stubs that use prototype map check to - // ensure that the prototype was not modified. - DCHECK(old_map_->is_prototype_map()); - DCHECK(old_map_->is_stable()); DCHECK(IsGeneralizableTo(old_details.constness(), new_constness_)); GeneralizeField(old_map_, modified_descriptor_, new_constness_, old_details.representation(), @@ -379,7 +449,8 @@ MapUpdater::State MapUpdater::FindTargetMap() { PropertyDetails details = target_descriptors->GetDetails(modified_descriptor_); DCHECK_EQ(new_kind_, details.kind()); - DCHECK_EQ(new_attributes_, details.attributes()); + 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())); @@ -398,9 +469,20 @@ MapUpdater::State MapUpdater::FindTargetMap() { if (*target_map_ != *old_map_) { old_map_->NotifyLeafMapLayoutChange(isolate_); } - result_map_ = target_map_; - state_ = kEnd; - return state_; // Done. + 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. @@ -572,10 +654,10 @@ Handle<DescriptorArray> MapUpdater::BuildDescriptorArray() { // 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 non in-place generalizable fields. - CHECK_IMPLIES(is_transitionable_fast_elements_kind_, - !Map::IsInplaceGeneralizableField( - next_constness, next_representation, *next_field_type)); + // 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)); @@ -653,7 +735,11 @@ MapUpdater::State MapUpdater::ConstructNewMap() { Handle<Map> split_map = FindSplitMap(new_descriptors); int split_nof = split_map->NumberOfOwnDescriptors(); - DCHECK_NE(old_nof_, split_nof); + 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); @@ -717,10 +803,31 @@ MapUpdater::State MapUpdater::ConstructNewMap() { split_map->ReplaceDescriptors(isolate_, *new_descriptors, *new_layout_descriptor); - result_map_ = new_map; - state_ = kEnd; + 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"); + + state_ = kEnd; + return state_; +} + } // namespace internal } // namespace v8 |