Drop Intrusive*TransitionIterators, use recursion instead.
We already use recursion to iterate over transition trees elsewhere, so this should be safe wrt. call stack height. Review URL: https://codereview.chromium.org/942523003 Cr-Commit-Position: refs/heads/master@{#26813}
This commit is contained in:
parent
2f7c192676
commit
32bac2df49
209
src/objects.cc
209
src/objects.cc
@ -7677,204 +7677,35 @@ void Map::RemoveFromCodeCache(Name* name, Code* code, int index) {
|
||||
}
|
||||
|
||||
|
||||
// An iterator over all map transitions in an descriptor array, reusing the
|
||||
// constructor field of the map while it is running. Negative values in
|
||||
// the constructor field indicate an active map transition iteration. The
|
||||
// original constructor is restored after iterating over all entries.
|
||||
class IntrusiveMapTransitionIterator {
|
||||
public:
|
||||
IntrusiveMapTransitionIterator(
|
||||
Map* map, TransitionArray* transition_array, Object* constructor)
|
||||
: map_(map),
|
||||
transition_array_(transition_array),
|
||||
constructor_(constructor) { }
|
||||
|
||||
void StartIfNotStarted() {
|
||||
DCHECK(!(*IteratorField())->IsSmi() || IsIterating());
|
||||
if (!(*IteratorField())->IsSmi()) {
|
||||
DCHECK(*IteratorField() == constructor_);
|
||||
*IteratorField() = Smi::FromInt(-1);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsIterating() {
|
||||
return (*IteratorField())->IsSmi() &&
|
||||
Smi::cast(*IteratorField())->value() < 0;
|
||||
}
|
||||
|
||||
Map* Next() {
|
||||
DCHECK(IsIterating());
|
||||
int value = Smi::cast(*IteratorField())->value();
|
||||
int index = -value - 1;
|
||||
int number_of_transitions = transition_array_->number_of_transitions();
|
||||
if (index < number_of_transitions) {
|
||||
*IteratorField() = Smi::FromInt(value - 1);
|
||||
return transition_array_->GetTarget(index);
|
||||
}
|
||||
|
||||
*IteratorField() = constructor_;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
Object** IteratorField() {
|
||||
return HeapObject::RawField(map_, Map::kConstructorOffset);
|
||||
}
|
||||
|
||||
Map* map_;
|
||||
TransitionArray* transition_array_;
|
||||
Object* constructor_;
|
||||
};
|
||||
|
||||
|
||||
// An iterator over all prototype transitions, reusing the constructor field
|
||||
// of the map while it is running. Positive values in the constructor field
|
||||
// indicate an active prototype transition iteration. The original constructor
|
||||
// is restored after iterating over all entries.
|
||||
class IntrusivePrototypeTransitionIterator {
|
||||
public:
|
||||
IntrusivePrototypeTransitionIterator(
|
||||
Map* map, HeapObject* proto_trans, Object* constructor)
|
||||
: map_(map), proto_trans_(proto_trans), constructor_(constructor) { }
|
||||
|
||||
void StartIfNotStarted() {
|
||||
if (!(*IteratorField())->IsSmi()) {
|
||||
DCHECK(*IteratorField() == constructor_);
|
||||
*IteratorField() = Smi::FromInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsIterating() {
|
||||
return (*IteratorField())->IsSmi() &&
|
||||
Smi::cast(*IteratorField())->value() >= 0;
|
||||
}
|
||||
|
||||
Map* Next() {
|
||||
DCHECK(IsIterating());
|
||||
int transitionNumber = Smi::cast(*IteratorField())->value();
|
||||
if (transitionNumber < NumberOfTransitions()) {
|
||||
*IteratorField() = Smi::FromInt(transitionNumber + 1);
|
||||
return GetTransition(transitionNumber);
|
||||
}
|
||||
*IteratorField() = constructor_;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
Object** IteratorField() {
|
||||
return HeapObject::RawField(map_, Map::kConstructorOffset);
|
||||
}
|
||||
|
||||
int NumberOfTransitions() {
|
||||
FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
|
||||
Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
|
||||
return Smi::cast(num)->value();
|
||||
}
|
||||
|
||||
Map* GetTransition(int transitionNumber) {
|
||||
FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
|
||||
int index = Map::kProtoTransitionHeaderSize + transitionNumber;
|
||||
return Map::cast(proto_trans->get(index));
|
||||
}
|
||||
|
||||
Map* map_;
|
||||
HeapObject* proto_trans_;
|
||||
Object* constructor_;
|
||||
};
|
||||
|
||||
|
||||
// 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 fields 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. We use the constructor field of the
|
||||
// map to store the current index. We can do that because the constructor
|
||||
// is the same for all involved maps.
|
||||
//
|
||||
// (a) If we currently follow normal map transitions, we temporarily store
|
||||
// the current index in the constructor field, and restore it to the
|
||||
// original constructor 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 constructor field, and restore it to the
|
||||
// original constructor 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 {
|
||||
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;
|
||||
}
|
||||
|
||||
// If we have an unvisited child map, return that one and advance. If we have
|
||||
// none, return NULL and restore the overwritten constructor field.
|
||||
TraversableMap* ChildIteratorNext(Object* constructor) {
|
||||
if (!HasTransitionArray()) return NULL;
|
||||
|
||||
TransitionArray* transition_array = transitions();
|
||||
if (transition_array->HasPrototypeTransitions()) {
|
||||
HeapObject* proto_transitions =
|
||||
transition_array->GetPrototypeTransitions();
|
||||
IntrusivePrototypeTransitionIterator proto_iterator(this,
|
||||
proto_transitions,
|
||||
constructor);
|
||||
proto_iterator.StartIfNotStarted();
|
||||
if (proto_iterator.IsIterating()) {
|
||||
Map* next = proto_iterator.Next();
|
||||
if (next != NULL) return static_cast<TraversableMap*>(next);
|
||||
static void TraverseTransitionTreeInternal(Map* map,
|
||||
Map::TraverseCallback callback,
|
||||
void* data) {
|
||||
if (map->HasTransitionArray()) {
|
||||
TransitionArray* transitions = map->transitions();
|
||||
if (transitions->HasPrototypeTransitions()) {
|
||||
FixedArray* proto_trans = transitions->GetPrototypeTransitions();
|
||||
Object* num_obj =
|
||||
proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
|
||||
int num = Smi::cast(num_obj)->value();
|
||||
for (int i = 0; i < num; ++i) {
|
||||
int index = Map::kProtoTransitionHeaderSize + i;
|
||||
TraverseTransitionTreeInternal(Map::cast(proto_trans->get(index)),
|
||||
callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
IntrusiveMapTransitionIterator transition_iterator(this,
|
||||
transition_array,
|
||||
constructor);
|
||||
transition_iterator.StartIfNotStarted();
|
||||
if (transition_iterator.IsIterating()) {
|
||||
Map* next = transition_iterator.Next();
|
||||
if (next != NULL) return static_cast<TraversableMap*>(next);
|
||||
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
|
||||
TraverseTransitionTreeInternal(transitions->GetTarget(i), callback, data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
callback(map, data);
|
||||
}
|
||||
|
||||
|
||||
// Traverse the transition tree in postorder without using the C++ stack by
|
||||
// doing pointer reversal.
|
||||
// Traverse the transition tree in postorder.
|
||||
void Map::TraverseTransitionTree(TraverseCallback callback, void* data) {
|
||||
// Make sure that we do not allocate in the callback.
|
||||
DisallowHeapAllocation no_allocation;
|
||||
|
||||
TraversableMap* current = static_cast<TraversableMap*>(this);
|
||||
// Get the root constructor here to restore it later when finished iterating
|
||||
// over maps.
|
||||
Object* root_constructor = constructor();
|
||||
while (true) {
|
||||
TraversableMap* child = current->ChildIteratorNext(root_constructor);
|
||||
if (child != NULL) {
|
||||
child->SetParent(current);
|
||||
current = child;
|
||||
} else {
|
||||
TraversableMap* parent = current->GetAndResetParent();
|
||||
callback(current, data);
|
||||
if (current == this) break;
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
TraverseTransitionTreeInternal(this, callback, data);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user