Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 2cdc689059898362b188889eaf37c5ab959eb1bd..13716b72b963604ad2b7083cd23e9f8283913ac9 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -2022,6 +2022,84 @@ void Map::LookupInDescriptors(JSObject* holder, |
} |
+static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents, |
+ ElementsKind elements_kind) { |
+ if (descriptor_contents->IsMap()) { |
+ Map* map = Map::cast(descriptor_contents); |
+ if (map->elements_kind() == elements_kind) { |
+ return map; |
+ } |
+ return NULL; |
+ } |
+ |
+ FixedArray* map_array = FixedArray::cast(descriptor_contents); |
+ for (int i = 0; i < map_array->length(); ++i) { |
+ Object* current = map_array->get(i); |
+ // Skip undefined slots, they are sentinels for reclaimed maps. |
+ if (!current->IsUndefined()) { |
+ Map* current_map = Map::cast(map_array->get(i)); |
+ if (current_map->elements_kind() == elements_kind) { |
+ return current_map; |
+ } |
+ } |
+ } |
+ |
+ return NULL; |
+} |
+ |
+ |
+static MaybeObject* AddElementsTransitionMapToDescriptor( |
+ Object* descriptor_contents, |
+ Map* new_map) { |
+ // Nothing was in the descriptor for an ELEMENTS_TRANSITION, |
+ // simply add the map. |
+ if (descriptor_contents == NULL) { |
+ return new_map; |
+ } |
+ |
+ // There was already a map in the descriptor, create a 2-element FixedArray |
+ // to contain the existing map plus the new one. |
+ FixedArray* new_array; |
+ Heap* heap = new_map->GetHeap(); |
+ if (descriptor_contents->IsMap()) { |
+ // Must tenure, DescriptorArray expects no new-space objects. |
+ MaybeObject* maybe_new_array = heap->AllocateFixedArray(2, TENURED); |
+ if (!maybe_new_array->To<FixedArray>(&new_array)) { |
+ return maybe_new_array; |
+ } |
+ new_array->set(0, descriptor_contents); |
+ new_array->set(1, new_map); |
+ return new_array; |
+ } |
+ |
+ // The descriptor already contained a list of maps for different ElementKinds |
+ // of ELEMENTS_TRANSITION, first check the existing array for an undefined |
+ // slot, and if that's not available, create a FixedArray to hold the existing |
+ // maps plus the new one and fill it in. |
+ FixedArray* array = FixedArray::cast(descriptor_contents); |
+ for (int i = 0; i < array->length(); ++i) { |
+ if (array->get(i)->IsUndefined()) { |
+ array->set(i, new_map); |
+ return array; |
+ } |
+ } |
+ |
+ // Must tenure, DescriptorArray expects no new-space objects. |
+ MaybeObject* maybe_new_array = |
+ heap->AllocateFixedArray(array->length() + 1, TENURED); |
+ if (!maybe_new_array->To<FixedArray>(&new_array)) { |
+ return maybe_new_array; |
+ } |
+ int i = 0; |
+ while (i < array->length()) { |
+ new_array->set(i, array->get(i)); |
+ ++i; |
+ } |
+ new_array->set(i, new_map); |
+ return new_array; |
+} |
+ |
+ |
MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) { |
Heap* current_heap = GetHeap(); |
Map* current_map = map(); |
@@ -2044,6 +2122,7 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) { |
safe_to_add_transition = false; |
} |
+ Object* descriptor_contents = NULL; |
if (safe_to_add_transition) { |
// It's only safe to manipulate the descriptor array if it would be |
// safe to add a transition. |
@@ -2063,9 +2142,15 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) { |
// return it. |
if (index != DescriptorArray::kNotFound) { |
PropertyDetails details(PropertyDetails(descriptors->GetDetails(index))); |
- if (details.type() == ELEMENTS_TRANSITION && |
- details.elements_kind() == elements_kind) { |
- return descriptors->GetValue(index); |
+ if (details.type() == ELEMENTS_TRANSITION) { |
+ descriptor_contents = descriptors->GetValue(index); |
+ Map* maybe_transition_map = |
+ GetElementsTransitionMapFromDescriptor(descriptor_contents, |
+ elements_kind); |
+ if (maybe_transition_map != NULL) { |
+ ASSERT(maybe_transition_map->IsMap()); |
+ return maybe_transition_map; |
+ } |
} else { |
safe_to_add_transition = false; |
} |
@@ -2085,15 +2170,19 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) { |
// Only remember the map transition if the object's map is NOT equal to the |
// global object_function's map and there is not an already existing |
// non-matching element transition. |
- bool allow_map_transition = |
- safe_to_add_transition && |
+ bool allow_map_transition = safe_to_add_transition && |
(GetIsolate()->context()->global_context()->object_function()->map() != |
map()); |
if (allow_map_transition) { |
- // Allocate new instance descriptors for the old map with map transition. |
+ MaybeObject* maybe_new_contents = |
+ AddElementsTransitionMapToDescriptor(descriptor_contents, new_map); |
+ Object* new_contents; |
+ if (!maybe_new_contents->ToObject(&new_contents)) { |
+ return maybe_new_contents; |
+ } |
+ |
ElementsTransitionDescriptor desc(elements_transition_sentinel_name, |
- Map::cast(new_map), |
- elements_kind); |
+ new_contents); |
Object* new_descriptors; |
MaybeObject* maybe_new_descriptors = descriptors->CopyInsert( |
&desc, |
@@ -6384,29 +6473,45 @@ void String::PrintOn(FILE* file) { |
} |
+void Map::CreateOneBackPointer(Map* target) { |
+#ifdef DEBUG |
+ // Verify target. |
+ Object* source_prototype = prototype(); |
+ Object* target_prototype = target->prototype(); |
+ ASSERT(source_prototype->IsJSReceiver() || |
+ source_prototype->IsMap() || |
+ source_prototype->IsNull()); |
+ ASSERT(target_prototype->IsJSReceiver() || |
+ target_prototype->IsNull()); |
+ ASSERT(source_prototype->IsMap() || |
+ source_prototype == target_prototype); |
+#endif |
+ // Point target back to source. set_prototype() will not let us set |
+ // the prototype to a map, as we do here. |
+ *RawField(target, kPrototypeOffset) = this; |
+} |
+ |
+ |
void Map::CreateBackPointers() { |
DescriptorArray* descriptors = instance_descriptors(); |
for (int i = 0; i < descriptors->number_of_descriptors(); i++) { |
if (descriptors->GetType(i) == MAP_TRANSITION || |
descriptors->GetType(i) == ELEMENTS_TRANSITION || |
descriptors->GetType(i) == CONSTANT_TRANSITION) { |
- // Get target. |
- Map* target = Map::cast(descriptors->GetValue(i)); |
-#ifdef DEBUG |
- // Verify target. |
- Object* source_prototype = prototype(); |
- Object* target_prototype = target->prototype(); |
- ASSERT(source_prototype->IsJSReceiver() || |
- source_prototype->IsMap() || |
- source_prototype->IsNull()); |
- ASSERT(target_prototype->IsJSReceiver() || |
- target_prototype->IsNull()); |
- ASSERT(source_prototype->IsMap() || |
- source_prototype == target_prototype); |
-#endif |
- // Point target back to source. set_prototype() will not let us set |
- // the prototype to a map, as we do here. |
- *RawField(target, kPrototypeOffset) = this; |
+ Object* object = reinterpret_cast<Object*>(descriptors->GetValue(i)); |
+ if (object->IsMap()) { |
+ CreateOneBackPointer(reinterpret_cast<Map*>(object)); |
+ } else { |
+ ASSERT(object->IsFixedArray()); |
+ ASSERT(descriptors->GetType(i) == ELEMENTS_TRANSITION); |
+ FixedArray* array = reinterpret_cast<FixedArray*>(object); |
+ for (int i = 0; i < array->length(); ++i) { |
+ Map* target = reinterpret_cast<Map*>(array->get(i)); |
+ if (!target->IsUndefined()) { |
+ CreateOneBackPointer(target); |
+ } |
+ } |
+ } |
} |
} |
} |
@@ -6433,17 +6538,46 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { |
if (details.type() == MAP_TRANSITION || |
details.type() == ELEMENTS_TRANSITION || |
details.type() == CONSTANT_TRANSITION) { |
- Map* target = reinterpret_cast<Map*>(contents->get(i)); |
- ASSERT(target->IsHeapObject()); |
- MarkBit map_mark = Marking::MarkBitFrom(target); |
- if (!map_mark.Get()) { |
- ASSERT(target->IsMap()); |
- contents->set_unchecked(i + 1, NullDescriptorDetails); |
- contents->set_null_unchecked(heap, i); |
- ASSERT(target->prototype() == this || |
- target->prototype() == real_prototype); |
- // Getter prototype() is read-only, set_prototype() has side effects. |
- *RawField(target, Map::kPrototypeOffset) = real_prototype; |
+ Object* object = reinterpret_cast<Object*>(contents->get(i)); |
+ if (object->IsMap()) { |
+ Map* target = reinterpret_cast<Map*>(object); |
+ ASSERT(target->IsHeapObject()); |
+ MarkBit map_mark = Marking::MarkBitFrom(target); |
+ if (!map_mark.Get()) { |
+ ASSERT(target->IsMap()); |
+ contents->set_unchecked(i + 1, NullDescriptorDetails); |
+ contents->set_null_unchecked(heap, i); |
+ ASSERT(target->prototype() == this || |
+ target->prototype() == real_prototype); |
+ // Getter prototype() is read-only, set_prototype() has side effects. |
+ *RawField(target, Map::kPrototypeOffset) = real_prototype; |
+ } |
+ } else { |
+ ASSERT(object->IsFixedArray()); |
+ ASSERT(details.type() == ELEMENTS_TRANSITION); |
+ FixedArray* array = reinterpret_cast<FixedArray*>(contents->get(i)); |
+ bool reachable_map_found = false; |
+ for (int j = 0; j < array->length(); ++j) { |
+ Map* target = reinterpret_cast<Map*>(array->get(j)); |
+ ASSERT(target->IsHeapObject()); |
+ MarkBit map_mark = Marking::MarkBitFrom(target); |
+ if (!map_mark.Get()) { |
+ ASSERT(target->IsMap()); |
+ array->set_undefined(j); |
+ ASSERT(target->prototype() == this || |
+ target->prototype() == real_prototype); |
+ // Getter prototype() is read-only, set_prototype() has side |
+ // effects. |
+ *RawField(target, Map::kPrototypeOffset) = real_prototype; |
+ } else { |
+ reachable_map_found = true; |
+ } |
+ } |
+ // If no map was found, make sure the FixedArray also gets collected. |
+ if (!reachable_map_found) { |
+ contents->set_unchecked(i + 1, NullDescriptorDetails); |
+ contents->set_null_unchecked(heap, i); |
+ } |
} |
} |
} |