Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 0941e792f04e98f3391c5a8757da74902d74e21e..a1d477156b434d01ca58be12ce584e6f8008054d 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -4935,75 +4935,191 @@ 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(); |
| - i += 2) { |
| - PropertyDetails details(Smi::cast(contents->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; |
| - } |
| - } |
| - 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; |
| +// An iterator over all map transitions in an descriptor array, reusing the map |
| +// field of the contens array while it is running. |
| +class IntrusiveMapTransitionIterator { |
| + public: |
| + explicit IntrusiveMapTransitionIterator(DescriptorArray* descriptor_array) |
| + : descriptor_array_(descriptor_array) { } |
| + |
| + void Start() { |
| + ASSERT(!IsIterating()); |
| + if (HasContentArray()) *ContentHeader() = Smi::FromInt(0); |
| + } |
| + |
| + bool IsIterating() { |
| + return HasContentArray() && (*ContentHeader())->IsSmi(); |
| + } |
| + |
| + Map* Next() { |
| + ASSERT(IsIterating()); |
| + FixedArray* contents = ContentArray(); |
| + int index = Smi::cast(*ContentHeader())->value(); |
| + while (index < contents->length()) { |
| + int next_index = index + 2; |
| + PropertyDetails details(Smi::cast(contents->get(index + 1))); |
| + if (details.IsTransition()) { |
| + *ContentHeader() = Smi::FromInt(next_index); |
| + return static_cast<Map*>(contents->get(index)); |
| } |
| + index = next_index; |
| } |
| - *proto_map_or_index_field = GetHeap()->fixed_array_map(); |
| - if (map_or_index_field != NULL) { |
| - *map_or_index_field = GetHeap()->fixed_array_map(); |
| + *ContentHeader() = descriptor_array_->GetHeap()->fixed_array_map(); |
| + return NULL; |
| + } |
| + |
| + private: |
| + bool HasContentArray() { |
| + return descriptor_array_-> length() > DescriptorArray::kContentArrayIndex; |
| + } |
| + |
| + FixedArray* ContentArray() { |
| + Object* array = descriptor_array_->get(DescriptorArray::kContentArrayIndex); |
| + return static_cast<FixedArray*>(array); |
| + } |
| + |
| + Object** ContentHeader() { |
| + return HeapObject::RawField(ContentArray(), DescriptorArray::kMapOffset); |
| + } |
| + |
| + DescriptorArray* descriptor_array_; |
| +}; |
| + |
| + |
| +// An iterator over all prototype transitions, reusing the map field of the |
| +// underlying array while it is running. |
| +class IntrusivePrototypeTransitionIterator { |
| + public: |
| + explicit IntrusivePrototypeTransitionIterator(FixedArray* proto_trans) |
| + : proto_trans_(proto_trans) { } |
| + |
| + void Start() { |
| + ASSERT(!IsIterating()); |
| + if (HasTransitions()) *Header() = Smi::FromInt(0); |
| + } |
| + |
| + bool IsIterating() { |
| + return HasTransitions() && (*Header())->IsSmi(); |
| + } |
| + |
| + Map* Next() { |
| + ASSERT(IsIterating()); |
| + int transitionNumber = Smi::cast(*Header())->value(); |
| + if (transitionNumber < NumberOfTransitions()) { |
| + *Header() = Smi::FromInt(transitionNumber + 1); |
| + return GetTransition(transitionNumber); |
| } |
| + *Header() = proto_trans_->GetHeap()->fixed_array_map(); |
| + return NULL; |
| + } |
| + |
| + private: |
| + bool HasTransitions() { |
| + return proto_trans_->length() >= Map::kProtoTransitionHeaderSize; |
| + } |
| + |
| + Object** Header() { |
| + return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset); |
| + } |
| + |
| + int NumberOfTransitions() { |
| + Object* num = proto_trans_->get(Map::kProtoTransitionNumberOfEntriesOffset); |
| + return Smi::cast(num)->value(); |
| + } |
| - // 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; |
| + Map* GetTransition(int transitionNumber) { |
| + return Map::cast(proto_trans_->get(IndexFor(transitionNumber))); |
| + } |
| + |
| + int IndexFor(int transitionNumber) { |
| + return Map::kProtoTransitionHeaderSize + |
| + Map::kProtoTransitionMapOffset + |
| + transitionNumber * Map::kProtoTransitionElementsPerEntry; |
| + } |
| + |
| + FixedArray* proto_trans_; |
| +}; |
| + |
| + |
| +// To traverse the transition tree iteratively, we have to store two kinds of |
| +// information in a map: The parent map in the traversal and which children of a |
| +// node have already been visited. To do this without additional memory, we |
| +// temporarily reuse two maps with known values: |
| +// |
| +// (1) The map of the map temporarily holds the parent, and is restored to the |
| +// meta map afterwards. |
| +// |
| +// (2) The info which children have already been visited depends on which part |
| +// of the map we currently iterate: |
| +// |
| +// (a) If we currently follow normal map transitions, we temporarily store |
| +// the current index in the map of the FixedArray of the desciptor |
| +// array's contents, and restore it to the fixed array map afterwards. |
| +// Note that a single descriptor can have 0, 1, or 2 transitions. |
| +// |
| +// (b) If we currently follow prototype transitions, we temporarily store |
| +// the current index in the map of the FixedArray holding the prototype |
| +// transitions, and restore it to the fixed array map afterwards. |
| +// |
| +// Note that the child iterator is just a concatenation of two iterators: One |
| +// iterating over map transitions and one iterating over prototype transisitons. |
| +class TraversableMap : public Map { |
|
Vyacheslav Egorov (Chromium)
2012/01/23 10:43:09
inheritance still.
|
| + public: |
| + // Record the parent in the traversal within this map. Note that this destroys |
| + // this map's map! |
| + void SetParent(TraversableMap* parent) { set_map_no_write_barrier(parent); } |
| + |
| + // Reset the current map's map, returning the parent previously stored in it. |
| + TraversableMap* GetAndResetParent() { |
| + TraversableMap* old_parent = static_cast<TraversableMap*>(map()); |
| + set_map_no_write_barrier(GetHeap()->meta_map()); |
| + return old_parent; |
| + } |
| + |
| + // Start iterating over this map's children, possibly destroying a FixedArray |
| + // map (see explanation above). |
| + void ChildIteratorStart() { |
| + IntrusiveMapTransitionIterator(instance_descriptors()).Start(); |
| + IntrusivePrototypeTransitionIterator( |
| + unchecked_prototype_transitions()).Start(); |
| + } |
| + |
| + // If we have an unvisited child map, return that one and advance. If we have |
| + // none, return NULL and reset any destroyed FixedArray maps. |
| + TraversableMap* ChildIteratorNext() { |
| + IntrusiveMapTransitionIterator descriptor_iterator(instance_descriptors()); |
| + if (descriptor_iterator.IsIterating()) { |
| + Map* next = descriptor_iterator.Next(); |
| + if (next != NULL) return static_cast<TraversableMap*>(next); |
| + } |
| + IntrusivePrototypeTransitionIterator |
| + proto_iterator(unchecked_prototype_transitions()); |
| + if (proto_iterator.IsIterating()) { |
| + Map* next = proto_iterator.Next(); |
| + if (next != NULL) return static_cast<TraversableMap*>(next); |
| + } |
| + return NULL; |
| + } |
| +}; |
| + |
| + |
| +// Traverse the transition tree in postorder without using the C++ stack by |
| +// doing pointer reversal. |
| +void Map::TraverseTransitionTree(TraverseCallback callback, void* data) { |
| + TraversableMap* current = static_cast<TraversableMap*>(this); |
| + current->ChildIteratorStart(); |
| + while (true) { |
| + TraversableMap* child = current->ChildIteratorNext(); |
| + if (child != NULL) { |
| + child->ChildIteratorStart(); |
| + child->SetParent(current); |
| + current = child; |
| + } else { |
| + TraversableMap* parent = current->GetAndResetParent(); |
| + callback(current, data); |
| + if (current == this) break; |
| + current = parent; |
| + } |
| } |
| } |