diff --git a/src/factory.cc b/src/factory.cc index 6f311ae5ba..a844bd1cdb 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1318,7 +1318,7 @@ Handle Factory::NewFunctionPrototype(Handle function) { // maps between prototypes of different constructors. Handle 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 prototype = NewJSObjectFromMap(new_map); diff --git a/src/objects-inl.h b/src/objects-inl.h index 301f177ef0..fb201f09ef 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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)); } diff --git a/src/objects.cc b/src/objects.cc index f334d56796..ebf184408f 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -7266,6 +7266,13 @@ Handle Map::CopyForObserved(Handle map) { } +Handle Map::CopyAsPrototypeMap(Handle map) { + Handle result = Copy(map); + result->mark_prototype_map(); + return result; +} + + Handle Map::Copy(Handle map) { Handle descriptors(map->instance_descriptors()); int number_of_own_descriptors = map->NumberOfOwnDescriptors(); @@ -10003,7 +10010,12 @@ void JSFunction::SetInstancePrototype(Handle function, // First some logic for the map of the prototype to make sure it is in fast // mode. if (value->IsJSObject()) { - JSObject::OptimizeAsPrototype(Handle::cast(value)); + Handle js_proto = Handle::cast(value); + JSObject::OptimizeAsPrototype(js_proto); + if (js_proto->HasFastProperties()) { + Handle 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 function) { Handle 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::cast(PrototypeIterator::GetCurrent(iter))); - } + // TODO(verwaest): Remove once "delete" keeps objects marked as prototypes + // fast as well. + JSObject::OptimizeAsPrototype(Handle::cast(prototype)); } else { prototype = isolate->factory()->NewFunctionPrototype(function); } diff --git a/src/objects.h b/src/objects.h index 69c10a1c8e..c86b3d3724 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 Copy(Handle map); + static Handle CopyAsPrototypeMap(Handle map); static Handle Create(Handle 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 {}; class ElementsKindBits: public BitField {}; // Derived values from bit field 2 diff --git a/test/mjsunit/fast-prototype.js b/test/mjsunit/fast-prototype.js index cdcc1a9ed6..55a0faae80 100644 --- a/test/mjsunit/fast-prototype.js +++ b/test/mjsunit/fast-prototype.js @@ -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;