Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 0941e792f04e98f3391c5a8757da74902d74e21e..07710ff134a014a4ef49f61f85be1d67cf1a547e 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -4935,75 +4935,193 @@ void Map::RemoveFromCodeCache(String* name, Code* code, int index) { |
} |
-void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { |
- // Traverse the transition tree without using a stack. We do this by |
- // reversing the pointers in the maps and descriptor arrays. |
- Map* current = this; |
- Map* meta_map = GetHeap()->meta_map(); |
- Object** map_or_index_field = NULL; |
- while (current != meta_map) { |
- DescriptorArray* d = reinterpret_cast<DescriptorArray*>( |
- *RawField(current, Map::kInstanceDescriptorsOrBitField3Offset)); |
- if (!d->IsEmpty()) { |
- FixedArray* contents = reinterpret_cast<FixedArray*>( |
- d->get(DescriptorArray::kContentArrayIndex)); |
- map_or_index_field = RawField(contents, HeapObject::kMapOffset); |
- Object* map_or_index = *map_or_index_field; |
- bool map_done = true; // Controls a nested continue statement. |
- for (int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : 0; |
- i < contents->length(); |
+class IntrusiveIterationState { |
+ public: |
+ IntrusiveIterationState(HeapObject* obj) |
+ : obj_(obj) { } |
+ |
+ Object* Get() { |
+ return *HeapObject::RawField(obj_, HeapObject::kMapOffset); |
+ } |
+ |
+ void Set(Object* s) { |
+ *HeapObject::RawField(obj_, HeapObject::kMapOffset) = s; |
+ } |
+ |
+ private: |
+ HeapObject* obj_; |
+}; |
+ |
+ |
+class IntrusiveFixedArrayIterator { |
+ public: |
+ IntrusiveFixedArrayIterator(FixedArray* array) |
+ : state_(array) { } |
+ |
+ bool IsIterating() { |
+ return state_.Get()->IsSmi(); |
+ } |
+ |
+ int GetIndex() { |
+ return Smi::cast(state_.Get())->value(); |
+ } |
+ |
+ void SetIndex(int index) { |
+ state_.Set(Smi::FromInt(index)); |
+ } |
+ |
+ void Stop() { |
+ state_.Set(HEAP->fixed_array_map()); |
+ } |
+ |
+ private: |
+ IntrusiveIterationState state_; |
+}; |
+ |
+ |
+class IntrusiveMapIterator { |
+ public: |
+ IntrusiveMapIterator(Map* map) |
+ : current_(map) { } |
+ |
+ void DescendInto(Map* child) { |
+ Map* parent = Current(); |
+ SetCurrent(child); |
+ ASSERT(Parent() == HEAP->meta_map()); |
+ SetParent(parent); |
+ } |
+ |
+ Map* Ascend() { |
+ // We are done with the current map. |
+ // Before ascending reset iteration state. |
+ StopMapTransitionsIteration(); |
+ StopPrototypeTransitionsIteration(); |
+ |
+ Map* parent = Parent(); |
+ SetParent(HEAP->meta_map()); |
+ |
+ Map* current = Current(); |
+ SetCurrent(parent); |
+ |
+ return current; |
+ } |
+ |
+ bool HasMore() { |
+ return Current() != HEAP->meta_map(); |
+ } |
+ |
+ |
+ Map* NextUnvisitedChild() { |
+ Map* child = NULL; |
+ |
+ child = NextUnvisitedMapTransition(); |
+ if (child != NULL) return child; |
+ |
+ child = NextUnvisitedPrototypeTransition(); |
+ if (child != NULL) return child; |
+ |
+ return NULL; |
+ } |
+ |
+ private: |
+ Map* current_; |
+ |
+ Map* Current() { |
+ return current_; |
+ } |
+ |
+ void SetCurrent(Map* current) { |
+ current_ = current; |
+ } |
+ |
+ Map* Parent() { |
+ return reinterpret_cast<Map*>(IntrusiveIterationState(current_).Get()); |
+ } |
+ |
+ void SetParent(Map* parent) { |
+ IntrusiveIterationState(current_).Set(parent); |
+ } |
+ |
+ FixedArray* ContentArray() { |
+ DescriptorArray* d = Current()->instance_descriptors(); |
+ return reinterpret_cast<FixedArray*>( |
+ d->get(DescriptorArray::kContentArrayIndex)); |
+ } |
+ |
+ bool HasContentArray() { |
+ return !Current()->instance_descriptors()->IsEmpty(); |
+ } |
+ |
+ void StopMapTransitionsIteration() { |
+ if (HasContentArray()) { |
+ IntrusiveFixedArrayIterator it(ContentArray()); |
+ it.Stop(); |
+ } |
+ } |
+ |
+ Map* NextUnvisitedMapTransition() { |
+ if (HasContentArray()) { |
+ FixedArray* content = ContentArray(); |
+ |
+ IntrusiveFixedArrayIterator it(content); |
+ for (int i = it.IsIterating() ? it.GetIndex() : 0; |
+ i < content->length(); |
i += 2) { |
- PropertyDetails details(Smi::cast(contents->get(i + 1))); |
+ PropertyDetails details(Smi::cast(content->get(i + 1))); |
if (details.IsTransition()) { |
// Found a map in the transition array. We record our progress in |
// the transition array by recording the current map in the map field |
// of the next map and recording the index in the transition array in |
// the map field of the array. |
- Map* next = Map::cast(contents->get(i)); |
- next->set_map_no_write_barrier(current); |
- *map_or_index_field = Smi::FromInt(i + 2); |
- current = next; |
- map_done = false; |
- break; |
+ Map* child = Map::cast(content->get(i)); |
+ it.SetIndex(i + 2); |
+ return child; |
} |
} |
- if (!map_done) continue; |
- } else { |
- map_or_index_field = NULL; |
- } |
- // That was the regular transitions, now for the prototype transitions. |
- FixedArray* prototype_transitions = |
- current->unchecked_prototype_transitions(); |
- Object** proto_map_or_index_field = |
- RawField(prototype_transitions, HeapObject::kMapOffset); |
- Object* map_or_index = *proto_map_or_index_field; |
- const int start = kProtoTransitionHeaderSize + kProtoTransitionMapOffset; |
- int i = map_or_index->IsSmi() ? Smi::cast(map_or_index)->value() : start; |
- if (i < prototype_transitions->length()) { |
- // Found a map in the prototype transition array. Record progress in |
- // an analogous way to the regular transitions array above. |
- Object* perhaps_map = prototype_transitions->get(i); |
- if (perhaps_map->IsMap()) { |
- Map* next = Map::cast(perhaps_map); |
- next->set_map_no_write_barrier(current); |
- *proto_map_or_index_field = |
- Smi::FromInt(i + kProtoTransitionElementsPerEntry); |
- current = next; |
- continue; |
- } |
} |
- *proto_map_or_index_field = GetHeap()->fixed_array_map(); |
- if (map_or_index_field != NULL) { |
- *map_or_index_field = GetHeap()->fixed_array_map(); |
+ return NULL; |
+ } |
+ |
+ void StopPrototypeTransitionsIteration() { |
+ FixedArray* array = Current()->unchecked_prototype_transitions(); |
+ IntrusiveFixedArrayIterator it(array); |
+ it.Stop(); |
+ } |
+ |
+ Map* NextUnvisitedPrototypeTransition() { |
+ FixedArray* array = Current()->unchecked_prototype_transitions(); |
+ IntrusiveFixedArrayIterator it(array); |
+ |
+ static const int kFirstMapIndex = |
+ (Map::kProtoTransitionHeaderSize + Map::kProtoTransitionMapOffset); |
+ |
+ int index = it.IsIterating() ? it.GetIndex() : kFirstMapIndex; |
+ |
+ if (index < array->length()) { |
+ Object* child = array->get(index); |
+ if (child->IsMap()) { |
+ it.SetIndex(index + Map::kProtoTransitionElementsPerEntry); |
+ return Map::cast(child); |
+ } |
} |
- // The callback expects a map to have a real map as its map, so we save |
- // the map field, which is being used to track the traversal and put the |
- // correct map (the meta_map) in place while we do the callback. |
- Map* prev = current->map(); |
- current->set_map_no_write_barrier(meta_map); |
- callback(current, data); |
- current = prev; |
+ return NULL; |
+ } |
+}; |
+ |
+ |
+void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { |
+ // Traverse the transition tree without using a stack. We do this by |
+ // reversing the pointers in the maps and descriptor arrays. |
+ IntrusiveMapIterator it(this); |
+ while (it.HasMore()) { |
+ Map* child = it.NextUnvisitedChild(); |
+ if (child != NULL) { |
+ it.DescendInto(child); |
+ } else { |
+ Map* map = it.Ascend(); |
+ callback(map, data); |
+ } |
} |
} |