Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index bef6dba7932e0297f3471f22b7829395c6379a24..f5905fa4f7ec8f610153e24057ca5e24624ac24c 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -2915,25 +2915,13 @@ static inline bool EqualImmutableValues(Object* obj1, Object* obj2) { |
} |
-// Invalidates a transition target at |key|, and installs |new_descriptors| over |
-// the current instance_descriptors to ensure proper sharing of descriptor |
-// arrays. |
-// Returns true if the transition target at given key was deprecated. |
-bool Map::DeprecateTarget(PropertyKind kind, Name* key, |
- PropertyAttributes attributes, |
- DescriptorArray* new_descriptors, |
- LayoutDescriptor* new_layout_descriptor) { |
- bool transition_target_deprecated = false; |
- Map* maybe_transition = |
- TransitionArray::SearchTransition(this, kind, key, attributes); |
- if (maybe_transition != NULL) { |
- maybe_transition->DeprecateTransitionTree(); |
- transition_target_deprecated = true; |
- } |
- |
+// Installs |new_descriptors| over the current instance_descriptors to ensure |
+// proper sharing of descriptor arrays. |
+void Map::ReplaceDescriptors(DescriptorArray* new_descriptors, |
+ LayoutDescriptor* new_layout_descriptor) { |
// Don't overwrite the empty descriptor array or initial map's descriptors. |
if (NumberOfOwnDescriptors() == 0 || GetBackPointer()->IsUndefined()) { |
- return transition_target_deprecated; |
+ return; |
} |
DescriptorArray* to_replace = instance_descriptors(); |
@@ -2947,7 +2935,6 @@ bool Map::DeprecateTarget(PropertyKind kind, Name* key, |
current = Map::cast(next); |
} |
set_owns_descriptors(false); |
- return transition_target_deprecated; |
} |
@@ -3613,9 +3600,6 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index, |
int split_nof = split_map->NumberOfOwnDescriptors(); |
DCHECK_NE(old_nof, split_nof); |
- Handle<LayoutDescriptor> new_layout_descriptor = |
- LayoutDescriptor::New(split_map, new_descriptors, old_nof); |
- |
PropertyKind split_kind; |
PropertyAttributes split_attributes; |
if (modify_index == split_nof) { |
@@ -3626,14 +3610,19 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index, |
split_kind = split_prop_details.kind(); |
split_attributes = split_prop_details.attributes(); |
} |
- bool transition_target_deprecated = split_map->DeprecateTarget( |
- split_kind, old_descriptors->GetKey(split_nof), split_attributes, |
- *new_descriptors, *new_layout_descriptor); |
- // If |transition_target_deprecated| is true then the transition array |
- // already contains entry for given descriptor. This means that the transition |
+ // 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 (!transition_target_deprecated && |
+ if (maybe_transition == NULL && |
!TransitionArray::CanHaveMoreTransitions(split_map)) { |
return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, |
new_kind, new_attributes, |
@@ -3664,13 +3653,16 @@ Handle<Map> Map::ReconfigureProperty(Handle<Map> old_map, int modify_index, |
*old_field_type, *new_field_type); |
} |
- // Add missing transitions. |
- Handle<Map> new_map = split_map; |
- for (int i = split_nof; i < old_nof; ++i) { |
- new_map = CopyInstallDescriptors(new_map, i, new_descriptors, |
- new_layout_descriptor); |
- } |
- new_map->set_owns_descriptors(true); |
+ 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; |
} |
@@ -9175,48 +9167,84 @@ Handle<Map> Map::CopyReplaceDescriptors( |
} |
-// Since this method is used to rewrite an existing transition tree, it can |
-// always insert transitions without checking. |
-Handle<Map> Map::CopyInstallDescriptors( |
- Handle<Map> map, int new_descriptor, Handle<DescriptorArray> descriptors, |
+// Creates transition tree starting from |split_map| and adding all descriptors |
+// starting from descriptor with index |split_map|.NumberOfOwnDescriptors(). |
+// The way how it is done is tricky because of GC and special descriptors |
+// marking logic. |
+Handle<Map> Map::AddMissingTransitions( |
+ Handle<Map> split_map, Handle<DescriptorArray> descriptors, |
Handle<LayoutDescriptor> full_layout_descriptor) { |
DCHECK(descriptors->IsSortedNoDuplicates()); |
+ int split_nof = split_map->NumberOfOwnDescriptors(); |
+ int nof_descriptors = descriptors->number_of_descriptors(); |
+ DCHECK_LT(split_nof, nof_descriptors); |
+ |
+ // Start with creating last map which will own full descriptors array. |
+ // This is necessary to guarantee that GC will mark the whole descriptor |
+ // array if any of the allocations happening below fail. |
+ // Number of unused properties is temporarily incorrect and the layout |
+ // descriptor could unnecessarily be in slow mode but we will fix after |
+ // all the other intermediate maps are created. |
+ Handle<Map> last_map = CopyDropDescriptors(split_map); |
+ last_map->InitializeDescriptors(*descriptors, *full_layout_descriptor); |
+ last_map->set_unused_property_fields(0); |
+ |
+ // During creation of intermediate maps we violate descriptors sharing |
+ // invariant since the last map is not yet connected to the transition tree |
+ // we create here. But it is safe because GC never trims map's descriptors |
+ // if there are no dead transitions from that map and this is exactly the |
+ // case for all the intermediate maps we create here. |
+ Handle<Map> map = split_map; |
+ for (int i = split_nof; i < nof_descriptors - 1; ++i) { |
+ Handle<Map> new_map = CopyDropDescriptors(map); |
+ InstallDescriptors(map, new_map, i, descriptors, full_layout_descriptor); |
+ map = new_map; |
+ } |
+ InstallDescriptors(map, last_map, nof_descriptors - 1, descriptors, |
+ full_layout_descriptor); |
+ return last_map; |
+} |
- Handle<Map> result = CopyDropDescriptors(map); |
- result->set_instance_descriptors(*descriptors); |
- result->SetNumberOfOwnDescriptors(new_descriptor + 1); |
+// Since this method is used to rewrite an existing transition tree, it can |
+// always insert transitions without checking. |
+void Map::InstallDescriptors(Handle<Map> parent, Handle<Map> child, |
+ int new_descriptor, |
+ Handle<DescriptorArray> descriptors, |
+ Handle<LayoutDescriptor> full_layout_descriptor) { |
+ DCHECK(descriptors->IsSortedNoDuplicates()); |
- int unused_property_fields = map->unused_property_fields(); |
+ child->set_instance_descriptors(*descriptors); |
+ child->SetNumberOfOwnDescriptors(new_descriptor + 1); |
+ |
+ int unused_property_fields = parent->unused_property_fields(); |
PropertyDetails details = descriptors->GetDetails(new_descriptor); |
if (details.location() == kField) { |
- unused_property_fields = map->unused_property_fields() - 1; |
+ unused_property_fields = parent->unused_property_fields() - 1; |
if (unused_property_fields < 0) { |
unused_property_fields += JSObject::kFieldsAdded; |
} |
} |
- result->set_unused_property_fields(unused_property_fields); |
+ child->set_unused_property_fields(unused_property_fields); |
if (FLAG_unbox_double_fields) { |
Handle<LayoutDescriptor> layout_descriptor = |
- LayoutDescriptor::AppendIfFastOrUseFull(map, details, |
+ LayoutDescriptor::AppendIfFastOrUseFull(parent, details, |
full_layout_descriptor); |
- result->set_layout_descriptor(*layout_descriptor); |
+ child->set_layout_descriptor(*layout_descriptor); |
#ifdef VERIFY_HEAP |
// TODO(ishell): remove these checks from VERIFY_HEAP mode. |
if (FLAG_verify_heap) { |
- CHECK(result->layout_descriptor()->IsConsistentWithMap(*result)); |
+ CHECK(child->layout_descriptor()->IsConsistentWithMap(*child)); |
} |
#else |
- SLOW_DCHECK(result->layout_descriptor()->IsConsistentWithMap(*result)); |
+ SLOW_DCHECK(child->layout_descriptor()->IsConsistentWithMap(*child)); |
#endif |
- result->set_visitor_id(Heap::GetStaticVisitorIdForMap(*result)); |
+ child->set_visitor_id(Heap::GetStaticVisitorIdForMap(*child)); |
} |
Handle<Name> name = handle(descriptors->GetKey(new_descriptor)); |
- ConnectTransition(map, result, name, SIMPLE_PROPERTY_TRANSITION); |
- |
- return result; |
+ ConnectTransition(parent, child, name, SIMPLE_PROPERTY_TRANSITION); |
} |