Fix prototype registration upon SlowToFast migration
authorjkummerow <jkummerow@chromium.org>
Tue, 28 Jul 2015 15:41:20 +0000 (08:41 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 28 Jul 2015 15:41:29 +0000 (15:41 +0000)
When a prototype object migrates from a slow to a fast map, where the slow map
was registered as a user of its own prototype, then the registration must be
transferred to the new map (just like MigrateToMap does for all other cases).

BUG=chromium:513602
LOG=y
NOTREECHECKS=true

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

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

src/objects.cc
test/mjsunit/regress/regress-crbug-513602.js [new file with mode: 0644]

index 06d05f4..fbf5ef5 100644 (file)
@@ -4644,20 +4644,32 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
     }
   }
 
-  int inobject_props = object->map()->inobject_properties();
+  Handle<Map> old_map(object->map(), isolate);
+
+  int inobject_props = old_map->inobject_properties();
 
   // Allocate new map.
-  Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
+  Handle<Map> new_map = Map::CopyDropDescriptors(old_map);
   new_map->set_dictionary_map(false);
 
-  if (object->map()->is_prototype_map()) {
+  if (old_map->is_prototype_map() && FLAG_track_prototype_users) {
     DCHECK(new_map->is_prototype_map());
-    new_map->set_prototype_info(object->map()->prototype_info());
-    object->map()->set_prototype_info(Smi::FromInt(0));
+
+    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*>(object->map()),
+             reinterpret_cast<void*>(*old_map),
              reinterpret_cast<void*>(*new_map));
     }
   }
@@ -4665,8 +4677,8 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
 #if TRACE_MAPS
   if (FLAG_trace_maps) {
     PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n",
-           reinterpret_cast<void*>(object->map()),
-           reinterpret_cast<void*>(*new_map), reason);
+           reinterpret_cast<void*>(*old_map), reinterpret_cast<void*>(*new_map),
+           reason);
   }
 #endif
 
diff --git a/test/mjsunit/regress/regress-crbug-513602.js b/test/mjsunit/regress/regress-crbug-513602.js
new file mode 100644 (file)
index 0000000..ae0441c
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function Parent() {}
+
+function Child() {}
+Child.prototype = new Parent();
+var child = new Child();
+
+function crash() {
+  return child.__proto__;
+}
+
+crash();
+crash();
+
+// Trigger a fast->slow->fast dance of Parent.prototype's map...
+Parent.prototype.__defineSetter__("foo", function() { print("A"); });
+Parent.prototype.__defineSetter__("foo", function() { print("B"); });
+// ...and collect more type feedback.
+crash();
+
+// Now modify the prototype chain. The right cell fails to get invalidated.
+delete Object.prototype.__proto__;
+crash();