Fasterify JSObject::UnregisterPrototypeUser
authorjkummerow <jkummerow@chromium.org>
Sat, 8 Aug 2015 22:56:15 +0000 (15:56 -0700)
committerCommit bot <commit-bot@chromium.org>
Sat, 8 Aug 2015 22:56:31 +0000 (22:56 +0000)
When a (prototype) map registers as a user of its own prototype, it now remembers the index in that prototype's registry where it is listed.
This remembered index is used on un-registration to find the right slot to clear without walking the entire registry.
Compaction of the registry must update all entries' remembered indices.

BUG=chromium:517778,chromium:517406
LOG=n
R=yangguo@chromium.org

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

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

src/factory.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
src/snapshot/serialize.cc
test/cctest/test-heap.cc

index 05cb259602edb00bcc9427dd75e69b82f9e30d55..e232d2c9254ffd2c854068746de97cdd1e4b8f8f 100644 (file)
@@ -55,6 +55,7 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
   Handle<PrototypeInfo> result =
       Handle<PrototypeInfo>::cast(NewStruct(PROTOTYPE_INFO_TYPE));
   result->set_prototype_users(WeakFixedArray::Empty());
+  result->set_registry_slot(PrototypeInfo::UNREGISTERED);
   result->set_validity_cell(Smi::FromInt(0));
   result->set_constructor_name(Smi::FromInt(0));
   return result;
index 21ad84990a8dbf703cebd58702a9009675013d29..fd0c6025a20b8547933499175a4ffca02b09b8a6 100644 (file)
@@ -4963,6 +4963,7 @@ ACCESSORS(ExecutableAccessorInfo, data, Object, kDataOffset)
 ACCESSORS(Box, value, Object, kValueOffset)
 
 ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset)
+SMI_ACCESSORS(PrototypeInfo, registry_slot, kRegistrySlotOffset)
 ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
 ACCESSORS(PrototypeInfo, constructor_name, Object, kConstructorNameOffset)
 
index 560419b0db3a4984afa8ee202088e5442098ae07..3414e2134d02c617157962bc254d7ffc366beea9 100644 (file)
@@ -967,6 +967,7 @@ 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 - registry slot: " << registry_slot();
   os << "\n - validity cell: " << Brief(validity_cell());
   os << "\n - constructor name: " << Brief(constructor_name());
   os << "\n";
index 8f3f1875e861853732fa8d79d42ac16145840803..3b3eb93d29d63c160284b45e06b6c9ed9b81eece 100644 (file)
@@ -1895,6 +1895,33 @@ bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields,
 }
 
 
+static void UpdatePrototypeUserRegistration(Handle<Map> old_map,
+                                            Handle<Map> new_map,
+                                            Isolate* isolate) {
+  if (!FLAG_track_prototype_users) return;
+  if (!old_map->is_prototype_map()) return;
+  DCHECK(new_map->is_prototype_map());
+  bool was_registered = JSObject::UnregisterPrototypeUser(old_map, isolate);
+  new_map->set_prototype_info(old_map->prototype_info());
+  old_map->set_prototype_info(Smi::FromInt(0));
+  if (FLAG_trace_prototype_users) {
+    PrintF("Moving prototype_info %p from map %p to map %p.\n",
+           reinterpret_cast<void*>(new_map->prototype_info()),
+           reinterpret_cast<void*>(*old_map),
+           reinterpret_cast<void*>(*new_map));
+  }
+  if (was_registered) {
+    if (new_map->prototype_info()->IsPrototypeInfo()) {
+      // The new map isn't registered with its prototype yet; reflect this fact
+      // in the PrototypeInfo it just inherited from the old map.
+      PrototypeInfo::cast(new_map->prototype_info())
+          ->set_registry_slot(PrototypeInfo::UNREGISTERED);
+    }
+    JSObject::LazyRegisterPrototypeUser(new_map, isolate);
+  }
+}
+
+
 void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
                             int expected_additional_properties) {
   if (object->map() == *new_map) return;
@@ -1908,16 +1935,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
   // when a map on a prototype chain is registered with its prototype, then
   // all prototypes further up the chain are also registered with their
   // respective prototypes.
-  Object* maybe_old_prototype = old_map->prototype();
-  if (FLAG_track_prototype_users && old_map->is_prototype_map() &&
-      maybe_old_prototype->IsJSObject()) {
-    Handle<JSObject> old_prototype(JSObject::cast(maybe_old_prototype));
-    bool was_registered =
-        JSObject::UnregisterPrototypeUser(old_prototype, old_map);
-    if (was_registered) {
-      JSObject::LazyRegisterPrototypeUser(new_map, new_map->GetIsolate());
-    }
-  }
+  UpdatePrototypeUserRegistration(old_map, new_map, new_map->GetIsolate());
 
   if (object->HasFastProperties()) {
     if (!new_map->is_dictionary_map()) {
@@ -1950,20 +1968,7 @@ void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map,
   // state now: the new map might have a new elements_kind, but the object's
   // elements pointer hasn't been updated yet. Callers will fix this, but in
   // the meantime, (indirectly) calling JSObjectVerify() must be avoided.
-  DisallowHeapAllocation no_object_verification;
-
-  if (old_map->is_prototype_map() && FLAG_track_prototype_users) {
-    DCHECK(new_map->is_prototype_map());
-    DCHECK(object->map() == *new_map);
-    new_map->set_prototype_info(old_map->prototype_info());
-    old_map->set_prototype_info(Smi::FromInt(0));
-    if (FLAG_trace_prototype_users) {
-      PrintF("Moving prototype_info %p from map %p to map %p.\n",
-             reinterpret_cast<void*>(new_map->prototype_info()),
-             reinterpret_cast<void*>(*old_map),
-             reinterpret_cast<void*>(*new_map));
-    }
-  }
+  // When adding code here, add a DisallowHeapAllocation too.
 }
 
 
@@ -4769,27 +4774,7 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
   Handle<Map> new_map = Map::CopyDropDescriptors(old_map);
   new_map->set_dictionary_map(false);
 
-  if (old_map->is_prototype_map() && FLAG_track_prototype_users) {
-    DCHECK(new_map->is_prototype_map());
-
-    Object* maybe_old_prototype = old_map->prototype();
-    if (maybe_old_prototype->IsJSObject()) {
-      Handle<JSObject> old_prototype(JSObject::cast(maybe_old_prototype));
-      bool was_registered =
-          JSObject::UnregisterPrototypeUser(old_prototype, old_map);
-      if (was_registered) {
-        JSObject::LazyRegisterPrototypeUser(new_map, isolate);
-      }
-    }
-    new_map->set_prototype_info(old_map->prototype_info());
-    old_map->set_prototype_info(Smi::FromInt(0));
-    if (FLAG_trace_prototype_users) {
-      PrintF("Moving prototype_info %p from map %p to map %p.\n",
-             reinterpret_cast<void*>(new_map->prototype_info()),
-             reinterpret_cast<void*>(*old_map),
-             reinterpret_cast<void*>(*new_map));
-    }
-  }
+  UpdatePrototypeUserRegistration(old_map, new_map, isolate);
 
 #if TRACE_MAPS
   if (FLAG_trace_maps) {
@@ -7938,59 +7923,46 @@ void WeakFixedArray::Set(Handle<WeakFixedArray> array, int index,
 
 
 // static
-Handle<WeakFixedArray> WeakFixedArray::Add(
-    Handle<Object> maybe_array, Handle<HeapObject> value,
-    SearchForDuplicates search_for_duplicates, bool* was_present) {
+Handle<WeakFixedArray> WeakFixedArray::Add(Handle<Object> maybe_array,
+                                           Handle<HeapObject> value,
+                                           int* assigned_index) {
   Handle<WeakFixedArray> array =
       (maybe_array.is_null() || !maybe_array->IsWeakFixedArray())
           ? Allocate(value->GetIsolate(), 1, Handle<WeakFixedArray>::null())
           : Handle<WeakFixedArray>::cast(maybe_array);
-  if (was_present != NULL) *was_present = false;
-  if (search_for_duplicates == kAddIfNotFound) {
-    for (int i = 0; i < array->Length(); ++i) {
-      if (array->Get(i) == *value) {
-        if (was_present != NULL) *was_present = true;
-        return array;
-      }
-    }
-#if 0  // Enable this if you want to check your search_for_duplicates flags.
-  } else {
-    for (int i = 0; i < array->Length(); ++i) {
-      DCHECK_NE(*value, array->Get(i));
-    }
-#endif
-  }
-
   // Try to store the new entry if there's room. Optimize for consecutive
   // accesses.
   int first_index = array->last_used_index();
-  if (array->Length() > 0) {
+  int length = array->Length();
+  if (length > 0) {
     for (int i = first_index;;) {
       if (array->IsEmptySlot((i))) {
         WeakFixedArray::Set(array, i, value);
+        if (assigned_index != NULL) *assigned_index = i;
         return array;
       }
       if (FLAG_trace_weak_arrays) {
         PrintF("[WeakFixedArray: searching for free slot]\n");
       }
-      i = (i + 1) % array->Length();
+      i = (i + 1) % length;
       if (i == first_index) break;
     }
   }
 
   // No usable slot found, grow the array.
-  int new_length =
-      array->Length() == 0 ? 1 : array->Length() + (array->Length() >> 1) + 4;
+  int new_length = length == 0 ? 1 : length + (length >> 1) + 4;
   Handle<WeakFixedArray> new_array =
       Allocate(array->GetIsolate(), new_length, array);
   if (FLAG_trace_weak_arrays) {
     PrintF("[WeakFixedArray: growing to size %d ]\n", new_length);
   }
-  WeakFixedArray::Set(new_array, array->Length(), value);
+  WeakFixedArray::Set(new_array, length, value);
+  if (assigned_index != NULL) *assigned_index = length;
   return new_array;
 }
 
 
+template <class CompactionCallback>
 void WeakFixedArray::Compact() {
   FixedArray* array = FixedArray::cast(this);
   int new_length = kFirstIndex;
@@ -7998,6 +7970,9 @@ void WeakFixedArray::Compact() {
     Object* element = array->get(i);
     if (element->IsSmi()) continue;
     if (WeakCell::cast(element)->cleared()) continue;
+    Object* value = WeakCell::cast(element)->value();
+    CompactionCallback::Callback(value, i - kFirstIndex,
+                                 new_length - kFirstIndex);
     array->set(new_length++, element);
   }
   array->Shrink(new_length);
@@ -8005,6 +7980,23 @@ void WeakFixedArray::Compact() {
 }
 
 
+void JSObject::PrototypeRegistryCompactionCallback::Callback(Object* value,
+                                                             int old_index,
+                                                             int new_index) {
+  DCHECK(value->IsMap() && Map::cast(value)->is_prototype_map());
+  Map* map = Map::cast(value);
+  DCHECK(map->prototype_info()->IsPrototypeInfo());
+  PrototypeInfo* proto_info = PrototypeInfo::cast(map->prototype_info());
+  DCHECK_EQ(old_index, proto_info->registry_slot());
+  proto_info->set_registry_slot(new_index);
+}
+
+
+template void WeakFixedArray::Compact<WeakFixedArray::NullCallback>();
+template void
+WeakFixedArray::Compact<JSObject::PrototypeRegistryCompactionCallback>();
+
+
 bool WeakFixedArray::Remove(Handle<HeapObject> value) {
   if (Length() == 0) return false;
   // Optimize for the most recently added element to be removed again.
@@ -8012,8 +8004,7 @@ bool WeakFixedArray::Remove(Handle<HeapObject> value) {
   for (int i = first_index;;) {
     if (Get(i) == *value) {
       Clear(i);
-      // Users of WeakFixedArray should make sure that there are no duplicates,
-      // they can use Add(..., kAddIfNotFound) if necessary.
+      // Users of WeakFixedArray should make sure that there are no duplicates.
       return true;
     }
     i = (i + 1) % Length();
@@ -9758,67 +9749,74 @@ void JSObject::LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
   DCHECK(user->is_prototype_map());
 
   Handle<Map> current_user = user;
+  Handle<PrototypeInfo> current_user_info =
+      Map::GetOrCreatePrototypeInfo(user, isolate);
   for (PrototypeIterator iter(user); !iter.IsAtEnd(); iter.Advance()) {
+    // Walk up the prototype chain as far as links haven't been registered yet.
+    if (current_user_info->registry_slot() != PrototypeInfo::UNREGISTERED) {
+      break;
+    }
     Handle<Object> maybe_proto = PrototypeIterator::GetCurrent(iter);
     if (maybe_proto->IsJSGlobalProxy()) continue;
     // Proxies on the prototype chain are not supported.
     if (maybe_proto->IsJSProxy()) return;
     Handle<JSObject> proto = Handle<JSObject>::cast(maybe_proto);
-    bool just_registered =
-        RegisterPrototypeUserIfNotRegistered(proto, current_user, isolate);
-    // Walk up the prototype chain as far as links haven't been registered yet.
-    if (!just_registered) break;
-    current_user = handle(proto->map(), isolate);
-  }
-}
-
+    Handle<PrototypeInfo> proto_info =
+        Map::GetOrCreatePrototypeInfo(proto, isolate);
+    Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
+    int slot = 0;
+    Handle<WeakFixedArray> new_array =
+        WeakFixedArray::Add(maybe_registry, current_user, &slot);
+    current_user_info->set_registry_slot(slot);
+    if (!maybe_registry.is_identical_to(new_array)) {
+      proto_info->set_prototype_users(*new_array);
+    }
+    if (FLAG_trace_prototype_users) {
+      PrintF("Registering %p as a user of prototype %p (map=%p).\n",
+             reinterpret_cast<void*>(*current_user),
+             reinterpret_cast<void*>(*proto),
+             reinterpret_cast<void*>(proto->map()));
+    }
 
-// Returns true if the user was not yet registered.
-// static
-bool JSObject::RegisterPrototypeUserIfNotRegistered(Handle<JSObject> prototype,
-                                                    Handle<HeapObject> user,
-                                                    Isolate* isolate) {
-  Handle<PrototypeInfo> proto_info =
-      Map::GetOrCreatePrototypeInfo(prototype, isolate);
-  Handle<Object> maybe_registry(proto_info->prototype_users(), isolate);
-  bool was_present = false;
-  Handle<WeakFixedArray> new_array = WeakFixedArray::Add(
-      maybe_registry, user, WeakFixedArray::kAddIfNotFound, &was_present);
-  if (!maybe_registry.is_identical_to(new_array)) {
-    proto_info->set_prototype_users(*new_array);
-  }
-  if (FLAG_trace_prototype_users && !was_present) {
-    PrintF("Registering %p as a user of prototype %p (map=%p).\n",
-           reinterpret_cast<void*>(*user), reinterpret_cast<void*>(*prototype),
-           reinterpret_cast<void*>(prototype->map()));
+    current_user = handle(proto->map(), isolate);
+    current_user_info = proto_info;
   }
-  return !was_present;
 }
 
 
 // Can be called regardless of whether |user| was actually registered with
 // |prototype|. Returns true when there was a registration.
 // static
-bool JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
-                                       Handle<HeapObject> user) {
-  Isolate* isolate = prototype->GetIsolate();
+bool JSObject::UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate) {
+  DCHECK(user->is_prototype_map());
+  // If it doesn't have a PrototypeInfo, it was never registered.
+  if (!user->prototype_info()->IsPrototypeInfo()) return false;
+  // If it doesn't have a prototype, it can't be registered.
+  if (!user->prototype()->IsJSObject()) return false;
+  Handle<JSObject> prototype(JSObject::cast(user->prototype()), isolate);
+  Handle<PrototypeInfo> user_info =
+      Map::GetOrCreatePrototypeInfo(user, isolate);
+  int slot = user_info->registry_slot();
+  if (slot == PrototypeInfo::UNREGISTERED) return false;
   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 false;
+  // User knows its registry slot, prototype info and user registry must exist.
+  DCHECK(maybe_proto_info->IsPrototypeInfo());
   Handle<PrototypeInfo> proto_info(PrototypeInfo::cast(maybe_proto_info),
                                    isolate);
   Object* maybe_registry = proto_info->prototype_users();
-  if (!maybe_registry->IsWeakFixedArray()) return false;
-  bool result = WeakFixedArray::cast(maybe_registry)->Remove(user);
-  if (FLAG_trace_prototype_users && result) {
+  DCHECK(maybe_registry->IsWeakFixedArray());
+  DCHECK(WeakFixedArray::cast(maybe_registry)->Get(slot) == *user);
+  WeakFixedArray::cast(maybe_registry)->Clear(slot);
+  if (FLAG_trace_prototype_users) {
     PrintF("Unregistering %p as a user of prototype %p.\n",
            reinterpret_cast<void*>(*user), reinterpret_cast<void*>(*prototype));
   }
-  return result;
+  return true;
 }
 
 
@@ -9879,6 +9877,19 @@ Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<JSObject> prototype,
 }
 
 
+// static
+Handle<PrototypeInfo> Map::GetOrCreatePrototypeInfo(Handle<Map> prototype_map,
+                                                    Isolate* isolate) {
+  Object* maybe_proto_info = prototype_map->prototype_info();
+  if (maybe_proto_info->IsPrototypeInfo()) {
+    return handle(PrototypeInfo::cast(maybe_proto_info), isolate);
+  }
+  Handle<PrototypeInfo> proto_info = isolate->factory()->NewPrototypeInfo();
+  prototype_map->set_prototype_info(*proto_info);
+  return proto_info;
+}
+
+
 // static
 Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
                                                         Isolate* isolate) {
@@ -10370,13 +10381,14 @@ void SharedFunctionInfo::SetScript(Handle<SharedFunctionInfo> shared,
     Handle<Script> script = Handle<Script>::cast(script_object);
     Handle<Object> list(script->shared_function_infos(), shared->GetIsolate());
 #ifdef DEBUG
-    bool found = false;
-    list = WeakFixedArray::Add(list, shared, WeakFixedArray::kAddIfNotFound,
-                               &found);
-    CHECK(!found);
-#else
-    list = WeakFixedArray::Add(list, shared, WeakFixedArray::kAlwaysAdd);
+    if (list->IsWeakFixedArray()) {
+      Handle<WeakFixedArray> array = Handle<WeakFixedArray>::cast(list);
+      for (int i = 0; i < array->Length(); ++i) {
+        DCHECK(array->Get(i) != *shared);
+      }
+    }
 #endif  // DEBUG
+    list = WeakFixedArray::Add(list, shared);
     script->set_shared_function_infos(*list);
   }
   // Finally set new script.
index dedb4d981b33319ab7c15f379b8b8efbb9d0006f..2edb687fa9bdaf486b5c403dcc7c80a149fc435b 100644 (file)
@@ -1906,13 +1906,15 @@ class JSObject: public JSReceiver {
                                   PrototypeOptimizationMode mode);
   static void ReoptimizeIfPrototype(Handle<JSObject> object);
   static void LazyRegisterPrototypeUser(Handle<Map> user, Isolate* isolate);
-  static bool RegisterPrototypeUserIfNotRegistered(Handle<JSObject> prototype,
-                                                   Handle<HeapObject> user,
-                                                   Isolate* isolate);
-  static bool UnregisterPrototypeUser(Handle<JSObject> prototype,
-                                      Handle<HeapObject> user);
+  static bool UnregisterPrototypeUser(Handle<Map> user, Isolate* isolate);
   static void InvalidatePrototypeChains(Map* map);
 
+  // Alternative implementation of WeakFixedArray::NullCallback.
+  class PrototypeRegistryCompactionCallback {
+   public:
+    static void Callback(Object* value, int old_index, int new_index);
+  };
+
   // Retrieve interceptors.
   InterceptorInfo* GetNamedInterceptor();
   InterceptorInfo* GetIndexedInterceptor();
@@ -2531,17 +2533,22 @@ class FixedDoubleArray: public FixedArrayBase {
 
 class WeakFixedArray : public FixedArray {
  public:
-  enum SearchForDuplicates { kAlwaysAdd, kAddIfNotFound };
-
   // If |maybe_array| is not a WeakFixedArray, a fresh one will be allocated.
-  static Handle<WeakFixedArray> Add(
-      Handle<Object> maybe_array, Handle<HeapObject> value,
-      SearchForDuplicates search_for_duplicates = kAlwaysAdd,
-      bool* was_present = NULL);
+  // This function does not check if the value exists already, callers must
+  // ensure this themselves if necessary.
+  static Handle<WeakFixedArray> Add(Handle<Object> maybe_array,
+                                    Handle<HeapObject> value,
+                                    int* assigned_index = NULL);
 
   // Returns true if an entry was found and removed.
   bool Remove(Handle<HeapObject> value);
 
+  class NullCallback {
+   public:
+    static void Callback(Object* value, int old_index, int new_index) {}
+  };
+
+  template <class CompactionCallback>
   void Compact();
 
   inline Object* Get(int index) const;
@@ -5467,6 +5474,8 @@ class Map: public HeapObject {
   // the given prototype's map).
   static Handle<PrototypeInfo> GetOrCreatePrototypeInfo(
       Handle<JSObject> prototype, Isolate* isolate);
+  static Handle<PrototypeInfo> GetOrCreatePrototypeInfo(
+      Handle<Map> prototype_map, Isolate* isolate);
 
   // [prototype chain validity cell]: Associated with a prototype object,
   // stored in that object's map's PrototypeInfo, indicates that prototype
@@ -6036,9 +6045,15 @@ class Box : public Struct {
 // Container for metadata stored on each prototype map.
 class PrototypeInfo : public Struct {
  public:
+  static const int UNREGISTERED = -1;
+
   // [prototype_users]: WeakFixedArray containing maps using this prototype,
   // or Smi(0) if uninitialized.
   DECL_ACCESSORS(prototype_users, Object)
+  // [registry_slot]: Slot in prototype's user registry where this user
+  // is stored. Returns UNREGISTERED if this prototype has not been registered.
+  inline int registry_slot() const;
+  inline void set_registry_slot(int slot);
   // [validity_cell]: Cell containing the validity bit for prototype chains
   // going through this object, or Smi(0) if uninitialized.
   DECL_ACCESSORS(validity_cell, Object)
@@ -6052,7 +6067,8 @@ class PrototypeInfo : public Struct {
   DECLARE_VERIFIER(PrototypeInfo)
 
   static const int kPrototypeUsersOffset = HeapObject::kHeaderSize;
-  static const int kValidityCellOffset = kPrototypeUsersOffset + kPointerSize;
+  static const int kRegistrySlotOffset = kPrototypeUsersOffset + kPointerSize;
+  static const int kValidityCellOffset = kRegistrySlotOffset + kPointerSize;
   static const int kConstructorNameOffset = kValidityCellOffset + kPointerSize;
   static const int kSize = kConstructorNameOffset + kPointerSize;
 
index fe9765064c8284404d0f948e09de6253e227ab7b..81e071c0fb05809d6b905fe8c5373bbb8e6bc5e6 100644 (file)
@@ -1926,7 +1926,23 @@ void Serializer::ObjectSerializer::Serialize() {
   if (object_->IsPrototypeInfo()) {
     Object* prototype_users = PrototypeInfo::cast(object_)->prototype_users();
     if (prototype_users->IsWeakFixedArray()) {
-      WeakFixedArray::cast(prototype_users)->Compact();
+      WeakFixedArray* array = WeakFixedArray::cast(prototype_users);
+      array->Compact<JSObject::PrototypeRegistryCompactionCallback>();
+    }
+  }
+  // Compaction of a prototype users list can require the registered users
+  // to update their remembered slots. That doesn't work if those users
+  // have already been serialized themselves. So if this object is a
+  // registered user, compact its prototype's user list now.
+  if (object_->IsMap()) {
+    Map* map = Map::cast(object_);
+    if (map->is_prototype_map() && map->prototype_info()->IsPrototypeInfo() &&
+        PrototypeInfo::cast(map->prototype_info())->registry_slot() !=
+            PrototypeInfo::UNREGISTERED) {
+      JSObject* proto = JSObject::cast(map->prototype());
+      PrototypeInfo* info = PrototypeInfo::cast(proto->map()->prototype_info());
+      WeakFixedArray* array = WeakFixedArray::cast(info->prototype_users());
+      array->Compact<JSObject::PrototypeRegistryCompactionCallback>();
     }
   }
 
@@ -1936,7 +1952,8 @@ void Serializer::ObjectSerializer::Serialize() {
     Script::cast(object_)->set_line_ends(undefined);
     Object* shared_list = Script::cast(object_)->shared_function_infos();
     if (shared_list->IsWeakFixedArray()) {
-      WeakFixedArray::cast(shared_list)->Compact();
+      WeakFixedArray::cast(shared_list)
+          ->Compact<WeakFixedArray::NullCallback>();
     }
   }
 
index 0bc3c1e13f6f4b47e124fbc38d5bf24a069ac76a..e15741cc2465e62c253a793add5fd9a436cbee92 100644 (file)
@@ -5954,7 +5954,7 @@ TEST(WeakFixedArray) {
   Handle<HeapNumber> number = CcTest::i_isolate()->factory()->NewHeapNumber(1);
   Handle<WeakFixedArray> array = WeakFixedArray::Add(Handle<Object>(), number);
   array->Remove(number);
-  array->Compact();
+  array->Compact<WeakFixedArray::NullCallback>();
   WeakFixedArray::Add(array, number);
 }