map->set_pre_allocated_property_fields(0);
map->init_instance_descriptors();
map->set_code_cache(empty_fixed_array(), SKIP_WRITE_BARRIER);
- map->init_prototype_transitions(undefined_value());
+ map->init_back_pointer(undefined_value());
map->set_unused_property_fields(0);
map->set_bit_field(0);
map->set_bit_field2(1 << Map::kIsExtensible);
// Fix the instance_descriptors for the existing maps.
meta_map()->init_instance_descriptors();
meta_map()->set_code_cache(empty_fixed_array());
- meta_map()->init_prototype_transitions(undefined_value());
+ meta_map()->init_back_pointer(undefined_value());
fixed_array_map()->init_instance_descriptors();
fixed_array_map()->set_code_cache(empty_fixed_array());
- fixed_array_map()->init_prototype_transitions(undefined_value());
+ fixed_array_map()->init_back_pointer(undefined_value());
oddball_map()->init_instance_descriptors();
oddball_map()->set_code_cache(empty_fixed_array());
- oddball_map()->init_prototype_transitions(undefined_value());
+ oddball_map()->init_back_pointer(undefined_value());
// Fix prototype object for existing maps.
meta_map()->set_prototype(null_value());
template <class T>
void Marker<T>::MarkMapContents(Map* map) {
- // Mark prototype transitions array but don't push it into marking stack.
- // This will make references from it weak. We will clean dead prototype
- // transitions in ClearNonLiveTransitions.
- Object** proto_trans_slot =
- HeapObject::RawField(map, Map::kPrototypeTransitionsOrBackPointerOffset);
- HeapObject* prototype_transitions = HeapObject::cast(*proto_trans_slot);
- if (prototype_transitions->IsFixedArray()) {
- mark_compact_collector()->RecordSlot(proto_trans_slot,
- proto_trans_slot,
- prototype_transitions);
- MarkBit mark = Marking::MarkBitFrom(prototype_transitions);
- if (!mark.Get()) {
- mark.Set();
- MemoryChunk::IncrementLiveBytesFromGC(prototype_transitions->address(),
- prototype_transitions->Size());
- }
- }
-
// Make sure that the back pointer stored either in the map itself or inside
// its prototype transitions array is marked. Treat pointers in the descriptor
// array as weak and also mark that array to prevent visiting it later.
// Mark the Object* fields of the Map. Since the descriptor array has been
// marked already, it is fine that one of these fields contains a pointer
- // to it. But make sure to skip back pointer and prototype transitions.
+ // to it. But make sure to skip back pointer.
STATIC_ASSERT(Map::kPointerFieldsEndOffset ==
- Map::kPrototypeTransitionsOrBackPointerOffset + kPointerSize);
- Object** start_slot = HeapObject::RawField(
- map, Map::kPointerFieldsBeginOffset);
- Object** end_slot = HeapObject::RawField(
- map, Map::kPrototypeTransitionsOrBackPointerOffset);
+ Map::kBackPointerOffset + kPointerSize);
+ Object** start_slot =
+ HeapObject::RawField(map, Map::kPointerFieldsBeginOffset);
+ Object** end_slot = HeapObject::RawField(map, Map::kBackPointerOffset);
for (Object** slot = start_slot; slot < end_slot; slot++) {
Object* obj = *slot;
if (!obj->NonFailureIsHeapObject()) continue;
Object** transitions_start = transitions->data_start();
if (transitions->HasElementsTransition()) {
- mark_compact_collector()->RecordSlot(transitions_start,
- transitions->GetElementsSlot(),
- transitions->elements_transition());
+ mark_compact_collector()->RecordSlot(
+ transitions_start,
+ transitions->GetElementsTransitionSlot(),
+ transitions->elements_transition());
+ }
+
+ if (transitions->HasPrototypeTransitions()) {
+ // Mark prototype transitions array but don't push it into marking stack.
+ // This will make references from it weak. We will clean dead prototype
+ // transitions in ClearNonLiveTransitions.
+ Object** proto_trans_slot = transitions->GetPrototypeTransitionsSlot();
+ HeapObject* prototype_transitions = HeapObject::cast(*proto_trans_slot);
+ base_marker()->MarkObjectWithoutPush(prototype_transitions);
+ mark_compact_collector()->RecordSlot(
+ transitions_start, proto_trans_slot, prototype_transitions);
}
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) {
int number_of_transitions = map->NumberOfProtoTransitions();
- FixedArray* prototype_transitions = map->prototype_transitions();
+ FixedArray* prototype_transitions = map->GetPrototypeTransitions();
int new_number_of_transitions = 0;
const int header = Map::kProtoTransitionHeaderSize;
void Map::ZapTransitions() {
TransitionArray* transition_array = transitions();
- if (transition_array == NULL) return;
MemsetPointer(transition_array->data_start(),
GetHeap()->the_hole_value(),
transition_array->length());
void Map::ZapPrototypeTransitions() {
- FixedArray* proto_transitions = prototype_transitions();
+ FixedArray* proto_transitions = GetPrototypeTransitions();
MemsetPointer(proto_transitions->data_start(),
GetHeap()->the_hole_value(),
proto_transitions->length());
Object* Map::GetBackPointer() {
- Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset);
- if (object->IsFixedArray()) {
- return FixedArray::cast(object)->get(kProtoTransitionBackPointerOffset);
- } else {
- return object;
- }
+ return READ_FIELD(this, kBackPointerOffset);
}
}
-// If the descriptor does not have a transition array, install a new
-// transition array that has room for an element transition.
-static MaybeObject* AllowElementsTransition(Map* map) {
+// If the descriptor is using the empty transition array, install a new empty
+// transition array that will have place for an element transition.
+static MaybeObject* EnsureHasTransitionArray(Map* map) {
if (map->HasTransitionArray()) return map;
AllowTransitions(map);
MaybeObject* Map::set_elements_transition_map(Map* transitioned_map) {
- MaybeObject* allow_elements = AllowElementsTransition(this);
+ MaybeObject* allow_elements = EnsureHasTransitionArray(this);
if (allow_elements->IsFailure()) return allow_elements;
transitions()->set_elements_transition(transitioned_map);
return this;
}
+FixedArray* Map::GetPrototypeTransitions() {
+ if (!HasTransitionArray()) return GetHeap()->empty_fixed_array();
+ if (!transitions()->HasPrototypeTransitions()) {
+ return GetHeap()->empty_fixed_array();
+ }
+ return transitions()->GetPrototypeTransitions();
+}
+
+
+MaybeObject* Map::SetPrototypeTransitions(FixedArray* proto_transitions) {
+ MaybeObject* allow_prototype = EnsureHasTransitionArray(this);
+ if (allow_prototype->IsFailure()) return allow_prototype;
+#ifdef DEBUG
+ if (HasPrototypeTransitions()) {
+ ASSERT(GetPrototypeTransitions() != proto_transitions);
+ ZapPrototypeTransitions();
+ }
+#endif
+ transitions()->SetPrototypeTransitions(proto_transitions);
+ return this;
+}
+
+
+bool Map::HasPrototypeTransitions() {
+ return HasTransitionArray() && transitions()->HasPrototypeTransitions();
+}
+
+
TransitionArray* Map::transitions() {
return instance_descriptors()->transitions();
}
}
+void Map::init_back_pointer(Object* undefined) {
+ ASSERT(undefined->IsUndefined());
+ WRITE_FIELD(this, kBackPointerOffset, undefined);
+}
+
+
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
Heap* heap = GetHeap();
ASSERT(instance_type() >= FIRST_JS_RECEIVER_TYPE);
ASSERT((value->IsUndefined() && GetBackPointer()->IsMap()) ||
(value->IsMap() && GetBackPointer()->IsUndefined()));
- Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset);
- if (object->IsFixedArray()) {
- FixedArray::cast(object)->set(
- kProtoTransitionBackPointerOffset, value, mode);
- } else {
- WRITE_FIELD(this, kPrototypeTransitionsOrBackPointerOffset, value);
- CONDITIONAL_WRITE_BARRIER(
- heap, this, kPrototypeTransitionsOrBackPointerOffset, value, mode);
- }
-}
-
-
-FixedArray* Map::prototype_transitions() {
- Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset);
- if (object->IsFixedArray()) {
- return FixedArray::cast(object);
- } else {
- return GetHeap()->empty_fixed_array();
- }
+ WRITE_FIELD(this, kBackPointerOffset, value);
+ CONDITIONAL_WRITE_BARRIER(heap, this, kBackPointerOffset, value, mode);
}
-void Map::set_prototype_transitions(FixedArray* value, WriteBarrierMode mode) {
- Heap* heap = GetHeap();
- ASSERT(value != heap->empty_fixed_array());
- value->set(kProtoTransitionBackPointerOffset, GetBackPointer());
-#ifdef DEBUG
- if (value != prototype_transitions()) {
- ZapPrototypeTransitions();
- }
-#endif
- WRITE_FIELD(this, kPrototypeTransitionsOrBackPointerOffset, value);
- CONDITIONAL_WRITE_BARRIER(
- heap, this, kPrototypeTransitionsOrBackPointerOffset, value, mode);
-}
-
-
-void Map::init_prototype_transitions(Object* undefined) {
- ASSERT(undefined->IsUndefined());
- WRITE_FIELD(this, kPrototypeTransitionsOrBackPointerOffset, undefined);
+// Can either be Smi (no transitions), normal transition array, or a transition
+// array with the header overwritten as a Smi (thus iterating).
+TransitionArray* Map::unchecked_transition_array() {
+ ASSERT(HasTransitionArray());
+ Object* object = *HeapObject::RawField(instance_descriptors(),
+ DescriptorArray::kTransitionsOffset);
+ ASSERT(!object->IsSmi());
+ TransitionArray* transition_array = static_cast<TransitionArray*>(object);
+ return transition_array;
}
-HeapObject* Map::unchecked_prototype_transitions() {
- Object* object = READ_FIELD(this, kPrototypeTransitionsOrBackPointerOffset);
- return reinterpret_cast<HeapObject*>(object);
+HeapObject* Map::UncheckedPrototypeTransitions() {
+ ASSERT(HasTransitionArray());
+ ASSERT(unchecked_transition_array()->HasPrototypeTransitions());
+ return unchecked_transition_array()->UncheckedPrototypeTransitions();
}
void Start() {
ASSERT(!IsIterating());
- if (HasTransitions()) *Header() = Smi::FromInt(0);
+ *Header() = Smi::FromInt(0);
}
bool IsIterating() {
- return HasTransitions() && (*Header())->IsSmi();
+ return (*Header())->IsSmi();
}
Map* Next() {
}
private:
- bool HasTransitions() {
- return proto_trans_->map()->IsSmi() || proto_trans_->IsFixedArray();
- }
-
Object** Header() {
return HeapObject::RawField(proto_trans_, FixedArray::kMapOffset);
}
int NumberOfTransitions() {
- ASSERT(HasTransitions());
FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
Object* num = proto_trans->get(Map::kProtoTransitionNumberOfEntriesOffset);
return Smi::cast(num)->value();
}
Map* GetTransition(int transitionNumber) {
- ASSERT(HasTransitions());
FixedArray* proto_trans = reinterpret_cast<FixedArray*>(proto_trans_);
return Map::cast(proto_trans->get(IndexFor(transitionNumber)));
}
return old_parent;
}
- // Can either be Smi (no instance descriptors), or a descriptor array with the
- // header overwritten as a Smi (thus iterating).
- TransitionArray* MutatedTransitions() {
- Object* object = *HeapObject::RawField(instance_descriptors(),
- DescriptorArray::kTransitionsOffset);
- TransitionArray* transition_array = static_cast<TransitionArray*>(object);
- return transition_array;
- }
-
// Start iterating over this map's children, possibly destroying a FixedArray
// map (see explanation above).
void ChildIteratorStart() {
if (HasTransitionArray()) {
+ if (HasPrototypeTransitions()) {
+ IntrusivePrototypeTransitionIterator(GetPrototypeTransitions()).Start();
+ }
+
IntrusiveMapTransitionIterator(transitions()).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() {
- IntrusivePrototypeTransitionIterator
- proto_iterator(unchecked_prototype_transitions());
- if (proto_iterator.IsIterating()) {
- Map* next = proto_iterator.Next();
- if (next != NULL) return static_cast<TraversableMap*>(next);
- }
if (HasTransitionArray()) {
- IntrusiveMapTransitionIterator
- transitions_iterator(MutatedTransitions());
- if (transitions_iterator.IsIterating()) {
- Map* next = transitions_iterator.Next();
+ TransitionArray* transition_array = unchecked_transition_array();
+
+ if (transition_array->HasPrototypeTransitions()) {
+ HeapObject* proto_transitions =
+ transition_array->UncheckedPrototypeTransitions();
+ IntrusivePrototypeTransitionIterator proto_iterator(proto_transitions);
+ if (proto_iterator.IsIterating()) {
+ Map* next = proto_iterator.Next();
+ if (next != NULL) return static_cast<TraversableMap*>(next);
+ }
+ }
+
+ IntrusiveMapTransitionIterator transition_iterator(transition_array);
+ if (transition_iterator.IsIterating()) {
+ Map* next = transition_iterator.Next();
if (next != NULL) return static_cast<TraversableMap*>(next);
}
}
+
return NULL;
}
};
// If the final transition array does not contain any live transitions, remove
// the transition array from the map.
- if (transition_index == 0 && !t->HasElementsTransition()) {
+ if (transition_index == 0 &&
+ !t->HasElementsTransition() &&
+ !t->HasPrototypeTransitions()) {
return ClearTransitions();
}
Map* Map::GetPrototypeTransition(Object* prototype) {
- FixedArray* cache = prototype_transitions();
+ FixedArray* cache = GetPrototypeTransitions();
int number_of_transitions = NumberOfProtoTransitions();
const int proto_offset =
kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
// Don't cache prototype transition if this map is shared.
if (is_shared() || !FLAG_cache_prototype_transitions) return this;
- FixedArray* cache = prototype_transitions();
+ FixedArray* cache = GetPrototypeTransitions();
const int step = kProtoTransitionElementsPerEntry;
const int header = kProtoTransitionHeaderSize;
new_cache->set(i + header, cache->get(i + header));
}
cache = new_cache;
- set_prototype_transitions(cache);
+ MaybeObject* set_result = SetPrototypeTransitions(cache);
+ if (set_result->IsFailure()) return set_result;
}
int last = transitions - 1;
inline Object* GetBackPointer();
inline void SetBackPointer(Object* value,
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline void init_back_pointer(Object* undefined);
// [prototype transitions]: cache of prototype transitions.
// Prototype transition is a transition that happens
// 1: back pointer that overlaps with prototype transitions field.
// 2 + 2 * i: prototype
// 3 + 2 * i: target map
- DECL_ACCESSORS(prototype_transitions, FixedArray)
+ inline FixedArray* GetPrototypeTransitions();
+ MUST_USE_RESULT inline MaybeObject* SetPrototypeTransitions(
+ FixedArray* prototype_transitions);
+ inline bool HasPrototypeTransitions();
- inline void init_prototype_transitions(Object* undefined);
- inline HeapObject* unchecked_prototype_transitions();
+ inline HeapObject* UncheckedPrototypeTransitions();
+ inline TransitionArray* unchecked_transition_array();
- static const int kProtoTransitionHeaderSize = 2;
+ static const int kProtoTransitionHeaderSize = 1;
static const int kProtoTransitionNumberOfEntriesOffset = 0;
- static const int kProtoTransitionBackPointerOffset = 1;
static const int kProtoTransitionElementsPerEntry = 2;
static const int kProtoTransitionPrototypeOffset = 0;
static const int kProtoTransitionMapOffset = 1;
inline int NumberOfProtoTransitions() {
- FixedArray* cache = prototype_transitions();
+ FixedArray* cache = GetPrototypeTransitions();
if (cache->length() == 0) return 0;
return
Smi::cast(cache->get(kProtoTransitionNumberOfEntriesOffset))->value();
}
inline void SetNumberOfProtoTransitions(int value) {
- FixedArray* cache = prototype_transitions();
+ FixedArray* cache = GetPrototypeTransitions();
ASSERT(cache->length() != 0);
cache->set_unchecked(kProtoTransitionNumberOfEntriesOffset,
Smi::FromInt(value));
kConstructorOffset + kPointerSize;
static const int kCodeCacheOffset =
kInstanceDescriptorsOrBitField3Offset + kPointerSize;
- static const int kPrototypeTransitionsOrBackPointerOffset =
- kCodeCacheOffset + kPointerSize;
- static const int kPadStart =
- kPrototypeTransitionsOrBackPointerOffset + kPointerSize;
+ static const int kBackPointerOffset = kCodeCacheOffset + kPointerSize;
+ static const int kPadStart = kBackPointerOffset + kPointerSize;
static const int kSize = MAP_POINTER_ALIGN(kPadStart);
// Layout of pointer fields. Heap iteration code relies on them
// being continuously allocated.
static const int kPointerFieldsBeginOffset = Map::kPrototypeOffset;
- static const int kPointerFieldsEndOffset =
- kPrototypeTransitionsOrBackPointerOffset + kPointerSize;
+ static const int kPointerFieldsEndOffset = kBackPointerOffset + kPointerSize;
// Byte offsets within kInstanceSizesOffset.
static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
"descriptors", map->instance_descriptors(),
Map::kInstanceDescriptorsOrBitField3Offset);
}
- if (map->unchecked_prototype_transitions()->IsFixedArray()) {
- TagObject(map->prototype_transitions(), "(prototype transitions)");
- SetInternalReference(map, entry,
- "prototype_transitions", map->prototype_transitions(),
- Map::kPrototypeTransitionsOrBackPointerOffset);
- } else {
- SetInternalReference(map, entry,
- "back_pointer", map->GetBackPointer(),
- Map::kPrototypeTransitionsOrBackPointerOffset);
- }
SetInternalReference(map, entry,
"code_cache", map->code_cache(),
Map::kCodeCacheOffset);
}
+bool TransitionArray::HasPrototypeTransitions() {
+ Object* prototype_transitions = get(kPrototypeTransitionsIndex);
+ return prototype_transitions != Smi::FromInt(0);
+}
+
+
+FixedArray* TransitionArray::GetPrototypeTransitions() {
+ Object* prototype_transitions = get(kPrototypeTransitionsIndex);
+ return FixedArray::cast(prototype_transitions);
+}
+
+
+HeapObject* TransitionArray::UncheckedPrototypeTransitions() {
+ Object* prototype_transitions = get(kPrototypeTransitionsIndex);
+ if (prototype_transitions == Smi::FromInt(0)) return NULL;
+ return reinterpret_cast<HeapObject*>(prototype_transitions);
+}
+
+
+void TransitionArray::SetPrototypeTransitions(FixedArray* transitions,
+ WriteBarrierMode mode) {
+ ASSERT(this != NULL);
+ ASSERT(transitions->IsFixedArray());
+ Heap* heap = GetHeap();
+ WRITE_FIELD(this, kPrototypeTransitionsOffset, transitions);
+ CONDITIONAL_WRITE_BARRIER(
+ heap, this, kPrototypeTransitionsOffset, transitions, mode);
+}
+
+
+Object** TransitionArray::GetPrototypeTransitionsSlot() {
+ return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
+ kPrototypeTransitionsOffset);
+}
+
+
Object** TransitionArray::GetKeySlot(int transition_number) {
ASSERT(transition_number < number_of_transitions());
return HeapObject::RawField(
}
-Object** TransitionArray::GetElementsSlot() {
+Object** TransitionArray::GetElementsTransitionSlot() {
return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
kElementsTransitionOffset);
}
}
array->set(kElementsTransitionIndex, Smi::FromInt(0));
+ array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
return array;
}
result->set_elements_transition(elements_transition());
}
+ if (HasPrototypeTransitions()) {
+ result->SetPrototypeTransitions(GetPrototypeTransitions());
+ }
+
FixedArray::WhitenessWitness witness(result);
if (insertion_index != kNotFound) {
// [length() - kTransitionSize] Last transition
class TransitionArray: public FixedArray {
public:
- inline Map* elements_transition();
- inline void set_elements_transition(
- Map* value,
- WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
- inline void ClearElementsTransition();
- inline bool HasElementsTransition();
// Accessors for fetching instance transition at transition number.
inline String* GetKey(int transition_number);
- inline Object** GetKeySlot(int transition_number);
inline void SetKey(int transition_number, String* value);
+ inline Object** GetKeySlot(int transition_number);
+
inline Object* GetValue(int transition_number);
- inline Object** GetValueSlot(int transition_number);
inline void SetValue(int transition_number, Object* value);
+ inline Object** GetValueSlot(int transition_number);
+
inline Map* GetTargetMap(int transition_number);
inline PropertyDetails GetTargetDetails(int transition_number);
- inline Object** GetElementsSlot();
+
+ inline Map* elements_transition();
+ inline void set_elements_transition(
+ Map* value,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline Object** GetElementsTransitionSlot();
+ inline bool HasElementsTransition();
+ inline void ClearElementsTransition();
+
+ inline FixedArray* GetPrototypeTransitions();
+ inline void SetPrototypeTransitions(
+ FixedArray* prototype_transitions,
+ WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
+ inline Object** GetPrototypeTransitionsSlot();
+ inline bool HasPrototypeTransitions();
+ inline HeapObject* UncheckedPrototypeTransitions();
// Returns the number of transitions in the array.
int number_of_transitions() {
static const int kNotFound = -1;
static const int kElementsTransitionIndex = 0;
- static const int kFirstIndex = 1;
+ static const int kPrototypeTransitionsIndex = 1;
+ static const int kFirstIndex = 2;
// Layout transition array header.
static const int kElementsTransitionOffset = FixedArray::kHeaderSize;
- static const int kFirstOffset = kElementsTransitionOffset + kPointerSize;
+ static const int kPrototypeTransitionsOffset = kElementsTransitionOffset +
+ kPointerSize;
+ static const int kFirstOffset = kPrototypeTransitionsOffset + kPointerSize;
// Layout of map transition.
static const int kTransitionKey = 0;
CHECK_EQ(transitions, baseObject->map()->NumberOfProtoTransitions());
// Verify that prototype transitions array was compacted.
- FixedArray* trans = baseObject->map()->prototype_transitions();
+ FixedArray* trans = baseObject->map()->GetPrototypeTransitions();
for (int i = 0; i < transitions; i++) {
int j = Map::kProtoTransitionHeaderSize +
i * Map::kProtoTransitionElementsPerEntry;
// clearing correctly records slots in prototype transition array.
i::FLAG_always_compact = true;
Handle<Map> map(baseObject->map());
- CHECK(!space->LastPage()->Contains(map->prototype_transitions()->address()));
+ CHECK(!space->LastPage()->Contains(
+ map->GetPrototypeTransitions()->address()));
CHECK(space->LastPage()->Contains(prototype->address()));
baseObject->SetPrototype(*prototype, false)->ToObjectChecked();
CHECK(map->GetPrototypeTransition(*prototype)->IsMap());