Keep function.prototype fast.
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 4 Aug 2014 15:02:07 +0000 (15:02 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 4 Aug 2014 15:02:07 +0000 (15:02 +0000)
BUG=
R=ishell@chromium.org

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

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

src/factory.cc
src/objects-inl.h
src/objects.cc
src/objects.h
test/mjsunit/fast-prototype.js

index 6f311ae..a844bd1 100644 (file)
@@ -1318,7 +1318,7 @@ Handle<JSObject> Factory::NewFunctionPrototype(Handle<JSFunction> function) {
     // maps between prototypes of different constructors.
     Handle<JSFunction> object_function(native_context->object_function());
     DCHECK(object_function->has_initial_map());
-    new_map = Map::Copy(handle(object_function->initial_map()));
+    new_map = Map::CopyAsPrototypeMap(handle(object_function->initial_map()));
   }
 
   Handle<JSObject> prototype = NewJSObjectFromMap(new_map);
index 301f177..fb201f0 100644 (file)
@@ -2115,6 +2115,7 @@ bool JSObject::HasFastProperties() {
 
 bool Map::TooManyFastProperties(StoreFromKeyed store_mode) {
   if (unused_property_fields() != 0) return false;
+  if (is_prototype_map()) return false;
   int minimum = store_mode == CERTAINLY_NOT_STORE_FROM_KEYED ? 128 : 12;
   int limit = Max(minimum, inobject_properties());
   int external = NumberOfFields() - inobject_properties();
@@ -4445,6 +4446,15 @@ bool Map::is_extensible() {
 }
 
 
+void Map::mark_prototype_map() {
+  set_bit_field2(IsPrototypeMapBits::update(bit_field2(), true));
+}
+
+bool Map::is_prototype_map() {
+  return IsPrototypeMapBits::decode(bit_field2());
+}
+
+
 void Map::set_is_shared(bool value) {
   set_bit_field3(IsShared::update(bit_field3(), value));
 }
index f334d56..ebf1844 100644 (file)
@@ -7266,6 +7266,13 @@ Handle<Map> Map::CopyForObserved(Handle<Map> map) {
 }
 
 
+Handle<Map> Map::CopyAsPrototypeMap(Handle<Map> map) {
+  Handle<Map> result = Copy(map);
+  result->mark_prototype_map();
+  return result;
+}
+
+
 Handle<Map> Map::Copy(Handle<Map> map) {
   Handle<DescriptorArray> descriptors(map->instance_descriptors());
   int number_of_own_descriptors = map->NumberOfOwnDescriptors();
@@ -10003,7 +10010,12 @@ void JSFunction::SetInstancePrototype(Handle<JSFunction> function,
   // First some logic for the map of the prototype to make sure it is in fast
   // mode.
   if (value->IsJSObject()) {
-    JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
+    Handle<JSObject> js_proto = Handle<JSObject>::cast(value);
+    JSObject::OptimizeAsPrototype(js_proto);
+    if (js_proto->HasFastProperties()) {
+      Handle<Map> new_map = Map::CopyAsPrototypeMap(handle(js_proto->map()));
+      JSObject::MigrateToMap(js_proto, new_map);
+    }
   }
 
   // Now some logic for the maps of the objects that are created by using this
@@ -10120,15 +10132,9 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
   Handle<Object> prototype;
   if (function->has_instance_prototype()) {
     prototype = handle(function->instance_prototype(), isolate);
-    for (PrototypeIterator iter(isolate, prototype,
-                                PrototypeIterator::START_AT_RECEIVER);
-         !iter.IsAtEnd(); iter.Advance()) {
-      if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
-        break;
-      }
-      JSObject::OptimizeAsPrototype(
-          Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)));
-    }
+    // TODO(verwaest): Remove once "delete" keeps objects marked as prototypes
+    // fast as well.
+    JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(prototype));
   } else {
     prototype = isolate->factory()->NewFunctionPrototype(function);
   }
index 69c10a1..c86b3d3 100644 (file)
@@ -6204,6 +6204,8 @@ class Map: public HeapObject {
 
   inline void set_is_extensible(bool value);
   inline bool is_extensible();
+  inline void mark_prototype_map();
+  inline bool is_prototype_map();
 
   inline void set_elements_kind(ElementsKind elements_kind) {
     DCHECK(elements_kind < kElementsKindCount);
@@ -6537,6 +6539,7 @@ class Map: public HeapObject {
   // Returns a copy of the map, with all transitions dropped from the
   // instance descriptors.
   static Handle<Map> Copy(Handle<Map> map);
+  static Handle<Map> CopyAsPrototypeMap(Handle<Map> map);
   static Handle<Map> Create(Handle<JSFunction> constructor,
                             int extra_inobject_properties);
 
@@ -6736,7 +6739,7 @@ class Map: public HeapObject {
   // Bit positions for bit field 2
   static const int kIsExtensible = 0;
   static const int kStringWrapperSafeForDefaultValueOf = 1;
-  // Currently bit 2 is not used.
+  class IsPrototypeMapBits : public BitField<bool, 2, 1> {};
   class ElementsKindBits: public BitField<ElementsKind, 3, 5> {};
 
   // Derived values from bit field 2
index cdcc1a9..55a0faa 100644 (file)
@@ -72,10 +72,15 @@ function test(use_new, add_first, set__proto__, same_map_as) {
     // Still fast
     assertTrue(%HasFastProperties(proto));
     AddProps(proto);
-    // After we add all those properties it went slow mode again :-(
-    assertFalse(%HasFastProperties(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) {
+  if (same_map_as && !add_first && set__proto__) {
     assertTrue(%HaveSameMap(same_map_as, proto));
   }
   return proto;