Move prototype metadata from internal properties to prototype maps
authorjkummerow <jkummerow@chromium.org>
Tue, 7 Apr 2015 10:42:45 +0000 (03:42 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 7 Apr 2015 10:42:57 +0000 (10:42 +0000)
The motivation is that we prefer to avoid creating internal properties, and we have a usable field on maps ("transitions", which is not used for prototype maps).
This CL also ensures the invariant that prototype maps are never shared, even if they are in dictionary mode.

Review URL: https://codereview.chromium.org/1033653002

Cr-Commit-Position: refs/heads/master@{#27617}

16 files changed:
include/v8.h
src/bootstrapper.cc
src/factory.cc
src/factory.h
src/heap-snapshot-generator.cc
src/heap/heap.h
src/heap/objects-visiting-inl.h
src/isolate.cc
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
src/transitions.cc
test/mjsunit/fast-prototype.js
test/mjsunit/regress/regress-put-prototype-transition.js

index 8b2f39d..b1da7a8 100644 (file)
@@ -6739,7 +6739,7 @@ class Internals {
   static const int kNodeIsIndependentShift = 3;
   static const int kNodeIsPartiallyDependentShift = 4;
 
-  static const int kJSObjectType = 0xbd;
+  static const int kJSObjectType = 0xbe;
   static const int kFirstNonstringType = 0x80;
   static const int kOddballType = 0x83;
   static const int kForeignType = 0x87;
index 93ebb3f..af75efd 100644 (file)
@@ -833,6 +833,7 @@ Handle<GlobalObject> Genesis::CreateNewGlobals(
         ApiNatives::GlobalObjectType);
   }
 
+  js_global_object_function->initial_map()->set_is_prototype_map(true);
   js_global_object_function->initial_map()->set_is_hidden_prototype();
   js_global_object_function->initial_map()->set_dictionary_map(true);
   Handle<GlobalObject> global_object =
index 1cdce60..2185e4d 100644 (file)
@@ -51,6 +51,15 @@ Handle<Box> Factory::NewBox(Handle<Object> value) {
 }
 
 
+Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
+  Handle<PrototypeInfo> result =
+      Handle<PrototypeInfo>::cast(NewStruct(PROTOTYPE_INFO_TYPE));
+  result->set_prototype_users(WeakFixedArray::Empty());
+  result->set_validity_cell(Smi::FromInt(0));
+  return result;
+}
+
+
 Handle<Oddball> Factory::NewOddball(Handle<Map> map,
                                     const char* to_string,
                                     Handle<Object> to_number,
index 0bb883d..3966a63 100644 (file)
@@ -58,6 +58,9 @@ class Factory FINAL {
   // Create a new boxed value.
   Handle<Box> NewBox(Handle<Object> value);
 
+  // Create a new PrototypeInfo struct.
+  Handle<PrototypeInfo> NewPrototypeInfo();
+
   // Create a pre-tenured empty AccessorPair.
   Handle<AccessorPair> NewAccessorPair();
 
index b8f9ab3..825e835 100644 (file)
@@ -1287,9 +1287,11 @@ void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
 
 
 void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
-  Object* raw_transitions = map->raw_transitions();
-  if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
-    TransitionArray* transitions = TransitionArray::cast(raw_transitions);
+  Object* raw_transitions_or_prototype_info = map->raw_transitions();
+  if (TransitionArray::IsFullTransitionArray(
+          raw_transitions_or_prototype_info)) {
+    TransitionArray* transitions =
+        TransitionArray::cast(raw_transitions_or_prototype_info);
     int transitions_entry = GetEntry(transitions)->index();
 
     if (FLAG_collect_maps && map->CanTransition()) {
@@ -1307,11 +1309,18 @@ void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
 
     TagObject(transitions, "(transition array)");
     SetInternalReference(map, entry, "transitions", transitions,
-                         Map::kTransitionsOffset);
-  } else if (TransitionArray::IsSimpleTransition(raw_transitions)) {
-    TagObject(raw_transitions, "(transition)");
-    SetInternalReference(map, entry, "transition", raw_transitions,
-                         Map::kTransitionsOffset);
+                         Map::kTransitionsOrPrototypeInfoOffset);
+  } else if (TransitionArray::IsSimpleTransition(
+                 raw_transitions_or_prototype_info)) {
+    TagObject(raw_transitions_or_prototype_info, "(transition)");
+    SetInternalReference(map, entry, "transition",
+                         raw_transitions_or_prototype_info,
+                         Map::kTransitionsOrPrototypeInfoOffset);
+  } else if (map->is_prototype_map()) {
+    TagObject(raw_transitions_or_prototype_info, "prototype_info");
+    SetInternalReference(map, entry, "prototype_info",
+                         raw_transitions_or_prototype_info,
+                         Map::kTransitionsOrPrototypeInfoOffset);
   }
   DescriptorArray* descriptors = map->instance_descriptors();
   TagObject(descriptors, "(map descriptors)");
index 8f5551a..889e3c4 100644 (file)
@@ -289,7 +289,6 @@ namespace internal {
   V(frozen_symbol)                  \
   V(nonexistent_symbol)             \
   V(elements_transition_symbol)     \
-  V(prototype_users_symbol)         \
   V(observed_symbol)                \
   V(uninitialized_symbol)           \
   V(megamorphic_symbol)             \
index 872b2dd..010bbfa 100644 (file)
@@ -585,10 +585,6 @@ template <typename StaticVisitor>
 void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
                                                           Map* map) {
   Object* raw_transitions = map->raw_transitions();
-  if (TransitionArray::IsSimpleTransition(raw_transitions)) {
-    StaticVisitor::VisitPointer(
-        heap, HeapObject::RawField(map, Map::kTransitionsOffset));
-  }
   if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
     MarkTransitionArray(heap, TransitionArray::cast(raw_transitions));
   }
index b491068..927972d 100644 (file)
@@ -756,14 +756,12 @@ void Isolate::ReportFailedAccessCheck(Handle<JSObject> receiver) {
 
 
 bool Isolate::IsInternallyUsedPropertyName(Handle<Object> name) {
-  return name.is_identical_to(factory()->hidden_string()) ||
-         name.is_identical_to(factory()->prototype_users_symbol());
+  return name.is_identical_to(factory()->hidden_string());
 }
 
 
 bool Isolate::IsInternallyUsedPropertyName(Object* name) {
-  return name == heap()->hidden_string() ||
-         name == heap()->prototype_users_symbol();
+  return name == heap()->hidden_string();
 }
 
 
index b352448..6969988 100644 (file)
@@ -879,6 +879,17 @@ void Box::BoxVerify() {
 }
 
 
+void PrototypeInfo::PrototypeInfoVerify() {
+  CHECK(IsPrototypeInfo());
+  if (prototype_users()->IsWeakFixedArray()) {
+    WeakFixedArray::cast(prototype_users())->FixedArrayVerify();
+  } else {
+    CHECK(prototype_users()->IsSmi());
+  }
+  CHECK(validity_cell()->IsCell() || validity_cell()->IsSmi());
+}
+
+
 void AccessorInfo::AccessorInfoVerify() {
   VerifyPointer(name());
   VerifyPointer(flag());
index 67d64c4..8d45d12 100644 (file)
@@ -4584,9 +4584,7 @@ void Map::set_unused_property_fields(int value) {
 }
 
 
-byte Map::bit_field() {
-  return READ_BYTE_FIELD(this, kBitFieldOffset);
-}
+byte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }
 
 
 void Map::set_bit_field(byte value) {
@@ -4594,9 +4592,7 @@ void Map::set_bit_field(byte value) {
 }
 
 
-byte Map::bit_field2() {
-  return READ_BYTE_FIELD(this, kBitField2Offset);
-}
+byte Map::bit_field2() const { return READ_BYTE_FIELD(this, kBitField2Offset); }
 
 
 void Map::set_bit_field2(byte value) {
@@ -4659,7 +4655,7 @@ void Map::set_is_prototype_map(bool value) {
   set_bit_field2(IsPrototypeMapBits::update(bit_field2(), value));
 }
 
-bool Map::is_prototype_map() {
+bool Map::is_prototype_map() const {
   return IsPrototypeMapBits::decode(bit_field2());
 }
 
@@ -5353,7 +5349,7 @@ void Map::set_bit_field3(uint32_t bits) {
 }
 
 
-uint32_t Map::bit_field3() {
+uint32_t Map::bit_field3() const {
   return READ_UINT32_FIELD(this, kBitField3Offset);
 }
 
@@ -5395,7 +5391,21 @@ Map* Map::ElementsTransitionMap() {
 }
 
 
-ACCESSORS(Map, raw_transitions, Object, kTransitionsOffset)
+ACCESSORS(Map, raw_transitions, Object, kTransitionsOrPrototypeInfoOffset)
+
+
+Object* Map::prototype_info() const {
+  DCHECK(is_prototype_map());
+  return READ_FIELD(this, Map::kTransitionsOrPrototypeInfoOffset);
+}
+
+
+void Map::set_prototype_info(Object* value, WriteBarrierMode mode) {
+  DCHECK(is_prototype_map());
+  WRITE_FIELD(this, Map::kTransitionsOrPrototypeInfoOffset, value);
+  CONDITIONAL_WRITE_BARRIER(
+      GetHeap(), this, Map::kTransitionsOrPrototypeInfoOffset, value, mode);
+}
 
 
 void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
@@ -5455,6 +5465,9 @@ ACCESSORS(ExecutableAccessorInfo, data, Object, kDataOffset)
 
 ACCESSORS(Box, value, Object, kValueOffset)
 
+ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset)
+ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
+
 ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
 ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
 
index 1b31c1b..cb3f27a 100644 (file)
@@ -416,7 +416,12 @@ void Map::MapPrint(std::ostream& os) {  // NOLINT
   os << " - unused property fields: " << unused_property_fields() << "\n";
   if (is_deprecated()) os << " - deprecated_map\n";
   if (is_dictionary_map()) os << " - dictionary_map\n";
-  if (is_prototype_map()) os << " - prototype_map\n";
+  if (is_prototype_map()) {
+    os << " - prototype_map\n";
+    os << " - prototype info: " << Brief(prototype_info());
+  } else {
+    os << " - back pointer: " << Brief(GetBackPointer());
+  }
   if (is_hidden_prototype()) os << " - hidden_prototype\n";
   if (has_named_interceptor()) os << " - named_interceptor\n";
   if (has_indexed_interceptor()) os << " - indexed_interceptor\n";
@@ -425,7 +430,6 @@ void Map::MapPrint(std::ostream& os) {  // NOLINT
   if (is_access_check_needed()) os << " - access_check_needed\n";
   if (!is_extensible()) os << " - non-extensible\n";
   if (is_observed()) os << " - observed\n";
-  os << " - back pointer: " << Brief(GetBackPointer());
   os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
      << "#" << NumberOfOwnDescriptors() << ": "
      << Brief(instance_descriptors());
@@ -873,6 +877,14 @@ void Box::BoxPrint(std::ostream& os) {  // NOLINT
 }
 
 
+void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) {  // NOLINT
+  HeapObject::PrintHeader(os, "PrototypeInfo");
+  os << "\n - prototype users: " << Brief(prototype_users());
+  os << "\n - validity cell: " << Brief(validity_cell());
+  os << "\n";
+}
+
+
 void AccessorPair::AccessorPairPrint(std::ostream& os) {  // NOLINT
   HeapObject::PrintHeader(os, "AccessorPair");
   os << "\n - getter: " << Brief(getter());
index 891aaac..f73bb37 100644 (file)
@@ -1902,11 +1902,12 @@ void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
 }
 
 
-void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
+void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
+                            int expected_additional_properties) {
   if (object->map() == *new_map) return;
+  Handle<Map> old_map(object->map());
   if (object->HasFastProperties()) {
     if (!new_map->is_dictionary_map()) {
-      Handle<Map> old_map(object->map());
       MigrateFastToFast(object, new_map);
       if (old_map->is_prototype_map()) {
         // Clear out the old descriptor array to avoid problems to sharing
@@ -1920,16 +1921,20 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
         DCHECK(new_map->GetBackPointer()->IsUndefined());
       }
     } else {
-      MigrateFastToSlow(object, new_map, 0);
+      MigrateFastToSlow(object, new_map, expected_additional_properties);
     }
   } else {
-    // For slow-to-fast migrations JSObject::TransformToFastProperties()
+    // For slow-to-fast migrations JSObject::MigrateSlowToFast()
     // must be used instead.
     CHECK(new_map->is_dictionary_map());
 
     // Slow-to-slow migration is trivial.
     object->set_map(*new_map);
   }
+  if (old_map->is_prototype_map()) {
+    new_map->set_prototype_info(old_map->prototype_info());
+    old_map->set_prototype_info(Smi::FromInt(0));
+  }
 }
 
 
@@ -4534,7 +4539,7 @@ void JSObject::NormalizeProperties(Handle<JSObject> object,
   Handle<Map> map(object->map());
   Handle<Map> new_map = Map::Normalize(map, mode, reason);
 
-  MigrateFastToSlow(object, new_map, expected_additional_properties);
+  MigrateToMap(object, new_map, expected_additional_properties);
 }
 
 
@@ -9946,6 +9951,7 @@ static bool PrototypeBenefitsFromNormalization(Handle<JSObject> object) {
 }
 
 
+// static
 void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
                                    PrototypeOptimizationMode mode) {
   if (object->IsGlobalObject()) return;
@@ -9955,14 +9961,12 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
     JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
                                   "NormalizeAsPrototype");
   }
-  bool has_just_copied_map = false;
+  Handle<Map> previous_map(object->map());
   if (!object->HasFastProperties()) {
     JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
-    has_just_copied_map = true;
   }
-  if (mode == FAST_PROTOTYPE && object->HasFastProperties() &&
-      !object->map()->is_prototype_map()) {
-    if (!has_just_copied_map) {
+  if (!object->map()->is_prototype_map()) {
+    if (object->map() == *previous_map) {
       Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
       JSObject::MigrateToMap(object, new_map);
     }
@@ -9985,39 +9989,54 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
 }
 
 
+// static
 void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
   if (!object->map()->is_prototype_map()) return;
   OptimizeAsPrototype(object, FAST_PROTOTYPE);
 }
 
 
+// static
 void JSObject::RegisterPrototypeUser(Handle<JSObject> prototype,
                                      Handle<HeapObject> user) {
   DCHECK(FLAG_track_prototype_users);
   Isolate* isolate = prototype->GetIsolate();
-  Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
-
-  // Get prototype users array, create it if it doesn't exist yet.
-  Handle<Object> maybe_array =
-      JSObject::GetProperty(prototype, symbol).ToHandleChecked();
-
-  Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_array, user);
-  if (!maybe_array.is_identical_to(new_array)) {
-    JSObject::SetOwnPropertyIgnoreAttributes(prototype, symbol, new_array,
-                                             DONT_ENUM).Assert();
+  if (prototype->IsJSGlobalProxy()) {
+    PrototypeIterator iter(isolate, prototype);
+    prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
+  }
+  Handle<PrototypeInfo> proto_info;
+  Object* maybe_proto_info = prototype->map()->prototype_info();
+  if (maybe_proto_info->IsPrototypeInfo()) {
+    proto_info = handle(PrototypeInfo::cast(maybe_proto_info), isolate);
+  } else {
+    proto_info = isolate->factory()->NewPrototypeInfo();
+    prototype->map()->set_prototype_info(*proto_info);
+  }
+  Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
+  Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_registry, user);
+  if (!maybe_registry.is_identical_to(new_array)) {
+    proto_info->set_prototype_users(*new_array);
   }
 }
 
 
+// static
 void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
                                        Handle<HeapObject> user) {
   Isolate* isolate = prototype->GetIsolate();
-  Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
-
-  Handle<Object> maybe_array =
-      JSObject::GetProperty(prototype, symbol).ToHandleChecked();
-  if (!maybe_array->IsWeakFixedArray()) return;
-  Handle<WeakFixedArray>::cast(maybe_array)->Remove(user);
+  if (prototype->IsJSGlobalProxy()) {
+    PrototypeIterator iter(isolate, prototype);
+    prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
+  }
+  DCHECK(prototype->map()->is_prototype_map());
+  Object* maybe_proto_info = prototype->map()->prototype_info();
+  if (!maybe_proto_info->IsPrototypeInfo()) return;
+  Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(maybe_proto_info),
+                                   isolate);
+  Object* maybe_registry = proto_info->prototype_users();
+  if (!maybe_registry->IsWeakFixedArray()) return;
+  WeakFixedArray::cast(maybe_registry)->Remove(user);
 }
 
 
@@ -10043,7 +10062,6 @@ void Map::SetPrototype(Handle<Object> prototype,
 bool Map::ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype) {
   if (!FLAG_track_prototype_users) return false;
   if (this->is_prototype_map()) return true;
-  if (this->is_dictionary_map()) return false;
   Object* back = GetBackPointer();
   if (!back->IsMap()) return true;
   if (Map::cast(back)->prototype() != *prototype) return true;
@@ -10051,14 +10069,6 @@ bool Map::ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype) {
 }
 
 
-bool Map::CanUseOptimizationsBasedOnPrototypeRegistry() {
-  if (!FLAG_track_prototype_users) return false;
-  if (this->is_prototype_map()) return true;
-  if (GetBackPointer()->IsMap()) return true;
-  return false;
-}
-
-
 Handle<Object> CacheInitialJSArrayMaps(
     Handle<Context> native_context, Handle<Map> initial_map) {
   // Replace all of the cached initial array maps in the native context with
index 49ad48c..022b6a8 100644 (file)
 //       - DebugInfo
 //       - BreakPointInfo
 //       - CodeCache
+//       - PrototypeInfo
 //     - WeakCell
 //
 // Formats of Object*:
@@ -420,6 +421,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
   V(TYPE_FEEDBACK_INFO_TYPE)                                    \
   V(ALIASED_ARGUMENTS_ENTRY_TYPE)                               \
   V(BOX_TYPE)                                                   \
+  V(PROTOTYPE_INFO_TYPE)                                        \
                                                                 \
   V(FIXED_ARRAY_TYPE)                                           \
   V(FIXED_DOUBLE_ARRAY_TYPE)                                    \
@@ -517,25 +519,27 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
 // Note that for subtle reasons related to the ordering or numerical values of
 // type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST
 // manually.
-#define STRUCT_LIST(V)                                                         \
-  V(BOX, Box, box)                                                             \
-  V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo, executable_accessor_info)\
-  V(ACCESSOR_PAIR, AccessorPair, accessor_pair)                                \
-  V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info)                     \
-  V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info)                       \
-  V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info)                     \
-  V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info)      \
-  V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info)            \
-  V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info)                        \
-  V(SCRIPT, Script, script)                                                    \
-  V(ALLOCATION_SITE, AllocationSite, allocation_site)                          \
-  V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento)                 \
-  V(CODE_CACHE, CodeCache, code_cache)                                         \
-  V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache)      \
-  V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info)                  \
-  V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry)   \
-  V(DEBUG_INFO, DebugInfo, debug_info)                                         \
-  V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)
+#define STRUCT_LIST(V)                                                       \
+  V(BOX, Box, box)                                                           \
+  V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo,                        \
+    executable_accessor_info)                                                \
+  V(ACCESSOR_PAIR, AccessorPair, accessor_pair)                              \
+  V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info)                   \
+  V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info)                     \
+  V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info)                   \
+  V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info)    \
+  V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info)          \
+  V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info)                      \
+  V(SCRIPT, Script, script)                                                  \
+  V(ALLOCATION_SITE, AllocationSite, allocation_site)                        \
+  V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento)               \
+  V(CODE_CACHE, CodeCache, code_cache)                                       \
+  V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache)    \
+  V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info)                \
+  V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
+  V(DEBUG_INFO, DebugInfo, debug_info)                                       \
+  V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)                      \
+  V(PROTOTYPE_INFO, PrototypeInfo, prototype_info)
 
 // We use the full 8 bits of the instance_type field to encode heap object
 // instance types.  The high-order bit (bit 7) is set if the object is not a
@@ -718,6 +722,7 @@ enum InstanceType {
   SHARED_FUNCTION_INFO_TYPE,
   WEAK_CELL_TYPE,
   PROPERTY_CELL_TYPE,
+  PROTOTYPE_INFO_TYPE,
 
   // All the following types are subtypes of JSReceiver, which corresponds to
   // objects in the JS sense. The first and the last type in this range are
@@ -2050,7 +2055,11 @@ class JSObject: public JSReceiver {
   static void TransitionElementsKind(Handle<JSObject> object,
                                      ElementsKind to_kind);
 
-  static void MigrateToMap(Handle<JSObject> object, Handle<Map> new_map);
+  // Always use this to migrate an object to a new map.
+  // |expected_additional_properties| is only used for fast-to-slow transitions
+  // and ignored otherwise.
+  static void MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
+                           int expected_additional_properties = 0);
 
   // Convert the object to use the canonical dictionary
   // representation. If the object is expected to have additional properties
@@ -2604,6 +2613,8 @@ class WeakFixedArray : public FixedArray {
   inline Object* Get(int index) const;
   inline int Length() const;
 
+  static Object* Empty() { return Smi::FromInt(0); }
+
   DECLARE_CAST(WeakFixedArray)
 
  private:
@@ -5839,15 +5850,15 @@ class Map: public HeapObject {
   inline void set_unused_property_fields(int value);
 
   // Bit field.
-  inline byte bit_field();
+  inline byte bit_field() const;
   inline void set_bit_field(byte value);
 
   // Bit field 2.
-  inline byte bit_field2();
+  inline byte bit_field2() const;
   inline void set_bit_field2(byte value);
 
   // Bit field 3.
-  inline uint32_t bit_field3();
+  inline uint32_t bit_field3() const;
   inline void set_bit_field3(uint32_t bits);
 
   class EnumLengthBits:             public BitField<int,
@@ -5942,7 +5953,7 @@ class Map: public HeapObject {
   inline void set_is_extensible(bool value);
   inline bool is_extensible();
   inline void set_is_prototype_map(bool value);
-  inline bool is_prototype_map();
+  inline bool is_prototype_map() const;
 
   inline void set_elements_kind(ElementsKind elements_kind) {
     DCHECK(static_cast<int>(elements_kind) < kElementsKindCount);
@@ -6013,6 +6024,9 @@ class Map: public HeapObject {
   // Don't call set_raw_transitions() directly to overwrite transitions, use
   // the TransitionArray::ReplaceTransitions() wrapper instead!
   DECL_ACCESSORS(raw_transitions, Object)
+  // [prototype_info]: Per-prototype metadata. Aliased with transitions
+  // (which prototype maps don't have).
+  DECL_ACCESSORS(prototype_info, Object)
 
   Map* FindRootMap();
   Map* FindFieldOwner(int descriptor);
@@ -6371,9 +6385,10 @@ class Map: public HeapObject {
   // otherwise a transition array is used.
   // For prototype maps, this slot is used to store a pointer to the prototype
   // object using this map.
-  static const int kTransitionsOffset =
+  static const int kTransitionsOrPrototypeInfoOffset =
       kConstructorOrBackPointerOffset + kPointerSize;
-  static const int kDescriptorsOffset = kTransitionsOffset + kPointerSize;
+  static const int kDescriptorsOffset =
+      kTransitionsOrPrototypeInfoOffset + kPointerSize;
 #if V8_DOUBLE_FIELDS_UNBOXING
   static const int kLayoutDecriptorOffset = kDescriptorsOffset + kPointerSize;
   static const int kCodeCacheOffset = kLayoutDecriptorOffset + kPointerSize;
@@ -6578,6 +6593,33 @@ class Box : public Struct {
 };
 
 
+// Container for metadata stored on each prototype map.
+class PrototypeInfo : public Struct {
+ public:
+  // [prototype_users]: WeakFixedArray containing maps using this prototype,
+  // or Smi(0) if uninitialized.
+  DECL_ACCESSORS(prototype_users, Object)
+  // [validity_cell]: Cell containing the validity bit for prototype chains
+  // going through this object, or Smi(0) if uninitialized.
+  DECL_ACCESSORS(validity_cell, Object)
+
+  DECLARE_CAST(PrototypeInfo)
+
+  // Dispatched behavior.
+  DECLARE_PRINTER(PrototypeInfo)
+  DECLARE_VERIFIER(PrototypeInfo)
+
+  static const int kPrototypeObjectOffset = HeapObject::kHeaderSize;
+  static const int kPrototypeUsersOffset =
+      kPrototypeObjectOffset + kPointerSize;
+  static const int kValidityCellOffset = kPrototypeUsersOffset + kPointerSize;
+  static const int kSize = kValidityCellOffset + kPointerSize;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(PrototypeInfo);
+};
+
+
 // Script describes a script which has been added to the VM.
 class Script: public Struct {
  public:
index 2e65e38..9fe9e86 100644 (file)
@@ -316,6 +316,8 @@ void TransitionArray::SetNumberOfPrototypeTransitions(
 int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
   if (CanStoreSimpleTransition(raw_transitions)) return 0;
   if (IsSimpleTransition(raw_transitions)) return 1;
+  // Prototype maps don't have transitions.
+  if (raw_transitions->IsPrototypeInfo()) return 0;
   DCHECK(IsFullTransitionArray(raw_transitions));
   return TransitionArray::cast(raw_transitions)->number_of_transitions();
 }
index c59ec94..2fb476c 100644 (file)
@@ -56,7 +56,7 @@ function DoProtoMagic(proto, set__proto__) {
 }
 
 
-function test(use_new, add_first, set__proto__, same_map_as) {
+function test(use_new, add_first, set__proto__) {
   var proto = use_new ? new Super() : {};
 
   // New object is fast.
@@ -74,16 +74,8 @@ function test(use_new, add_first, set__proto__, same_map_as) {
     // Still fast
     assertTrue(%HasFastProperties(proto));
     AddProps(proto);
-    if (set__proto__) {
-      // After we add all those properties it went slow mode again :-(
-      assertFalse(%HasFastProperties(proto));
-    } else {
-      // .prototype keeps it fast.
-      assertTrue(%HasFastProperties(proto));
-    }
-  }
-  if (same_map_as && !add_first && set__proto__) {
-    assertTrue(%HaveSameMap(same_map_as, proto));
+    // Still fast.
+    assertTrue(%HasFastProperties(proto));
   }
   return proto;
 }
@@ -96,9 +88,7 @@ for (var i = 0; i < 4; i++) {
   var use_new = ((i & 2) != 0);
 
   test(use_new, true, set__proto__);
-
-  var last = test(use_new, false, set__proto__);
-  test(use_new, false, set__proto__, last);
+  test(use_new, false, set__proto__);
 }
 
 
index f720bd6..70f0074 100644 (file)
@@ -35,7 +35,7 @@ function __f_1(__v_4, add_first, __v_6, same_map_as) {
     __f_0(__v_1, __v_6);
     assertTrue(%HasFastProperties(__v_1));
     __f_4(__v_1);
-    assertFalse(%HasFastProperties(__v_1));
+    assertTrue(%HasFastProperties(__v_1));
   }
 }
 gc();