summaryrefslogtreecommitdiff
path: root/deps/v8/src/map-updater.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/map-updater.cc')
-rw-r--r--deps/v8/src/map-updater.cc161
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