Moving prototype transitions into the header of the transition array.
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 10 Jul 2012 07:53:00 +0000 (07:53 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 10 Jul 2012 07:53:00 +0000 (07:53 +0000)
Review URL: https://chromiumcodereview.appspot.com/10692026

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12022 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/heap.cc
src/mark-compact.cc
src/objects-debug.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/profile-generator.cc
src/transitions-inl.h
src/transitions.cc
src/transitions.h
test/cctest/test-heap.cc

index 5c3a386..1b4ec81 100644 (file)
@@ -2082,7 +2082,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type,
   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);
@@ -2221,15 +2221,15 @@ bool Heap::CreateInitialMaps() {
   // 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());
index a9d0c16..e75dc49 100644 (file)
@@ -1823,24 +1823,6 @@ template void Marker<MarkCompactCollector>::MarkMapContents(Map* map);
 
 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.
@@ -1855,13 +1837,12 @@ void Marker<T>::MarkMapContents(Map* map) {
 
   // 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;
@@ -1952,9 +1933,21 @@ void Marker<T>::MarkTransitionArray(TransitionArray* transitions) {
   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) {
@@ -2545,7 +2538,7 @@ void MarkCompactCollector::ClearNonLiveTransitions() {
 
 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;
index fde1814..e40fe51 100644 (file)
@@ -1006,7 +1006,6 @@ void NormalizedMapCache::NormalizedMapCacheVerify() {
 
 void Map::ZapTransitions() {
   TransitionArray* transition_array = transitions();
-  if (transition_array == NULL) return;
   MemsetPointer(transition_array->data_start(),
                 GetHeap()->the_hole_value(),
                 transition_array->length());
@@ -1014,7 +1013,7 @@ void Map::ZapTransitions() {
 
 
 void Map::ZapPrototypeTransitions() {
-  FixedArray* proto_transitions = prototype_transitions();
+  FixedArray* proto_transitions = GetPrototypeTransitions();
   MemsetPointer(proto_transitions->data_start(),
                 GetHeap()->the_hole_value(),
                 proto_transitions->length());
index 55a7a42..06c3405 100644 (file)
@@ -3524,12 +3524,7 @@ void Map::set_bit_field3(int value) {
 
 
 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);
 }
 
 
@@ -3567,9 +3562,9 @@ static MaybeObject* AllowTransitions(Map* map) {
 }
 
 
-// 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);
@@ -3584,13 +3579,41 @@ static MaybeObject* AllowElementsTransition(Map* 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();
 }
@@ -3623,57 +3646,38 @@ MaybeObject* Map::set_transitions(TransitionArray* transitions_array) {
 }
 
 
+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();
 }
 
 
index 244e22b..9861988 100644 (file)
@@ -5147,11 +5147,11 @@ class IntrusivePrototypeTransitionIterator {
 
   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() {
@@ -5166,23 +5166,17 @@ class IntrusivePrototypeTransitionIterator {
   }
 
  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)));
   }
@@ -5232,42 +5226,41 @@ class TraversableMap : public Map {
     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;
   }
 };
@@ -7417,7 +7410,9 @@ void Map::ClearNonLiveTransitions(Heap* heap) {
 
   // 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();
   }
 
@@ -8835,7 +8830,7 @@ MaybeObject* JSArray::SetElementsLength(Object* len) {
 
 
 Map* Map::GetPrototypeTransition(Object* prototype) {
-  FixedArray* cache = prototype_transitions();
+  FixedArray* cache = GetPrototypeTransitions();
   int number_of_transitions = NumberOfProtoTransitions();
   const int proto_offset =
       kProtoTransitionHeaderSize + kProtoTransitionPrototypeOffset;
@@ -8857,7 +8852,7 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
   // 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;
@@ -8880,7 +8875,8 @@ MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
       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;
index b697605..d7cd80f 100644 (file)
@@ -4864,6 +4864,7 @@ class Map: public HeapObject {
   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
@@ -4873,27 +4874,29 @@ class Map: public HeapObject {
   //    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));
@@ -5053,17 +5056,14 @@ class Map: public HeapObject {
       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;
index ea93006..d06b62c 100644 (file)
@@ -2013,16 +2013,6 @@ void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
                          "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);
index 9061883..517fdc8 100644 (file)
@@ -82,6 +82,42 @@ void TransitionArray::set_elements_transition(Map* transition_map,
 }
 
 
+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(
@@ -148,7 +184,7 @@ PropertyDetails TransitionArray::GetTargetDetails(int transition_number) {
 }
 
 
-Object** TransitionArray::GetElementsSlot() {
+Object** TransitionArray::GetElementsTransitionSlot() {
   return HeapObject::RawField(reinterpret_cast<HeapObject*>(this),
                               kElementsTransitionOffset);
 }
index 033f224..1e965cd 100644 (file)
@@ -45,6 +45,7 @@ MaybeObject* TransitionArray::Allocate(int number_of_transitions) {
   }
 
   array->set(kElementsTransitionIndex, Smi::FromInt(0));
+  array->set(kPrototypeTransitionsIndex, Smi::FromInt(0));
   return array;
 }
 
@@ -98,6 +99,10 @@ MaybeObject* TransitionArray::CopyInsert(String* name, Object* value) {
     result->set_elements_transition(elements_transition());
   }
 
+  if (HasPrototypeTransitions()) {
+    result->SetPrototypeTransitions(GetPrototypeTransitions());
+  }
+
   FixedArray::WhitenessWitness witness(result);
 
   if (insertion_index != kNotFound) {
index 5abdf99..a4b5b27 100644 (file)
@@ -46,22 +46,33 @@ namespace internal {
 // [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() {
@@ -99,11 +110,14 @@ class TransitionArray: public FixedArray {
   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;
index 211a20c..8ba7f40 100644 (file)
@@ -1587,7 +1587,7 @@ TEST(PrototypeTransitionClearing) {
   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;
@@ -1608,7 +1608,8 @@ TEST(PrototypeTransitionClearing) {
   // 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());