Move prototype metadata from internal properties to prototype maps

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}
This commit is contained in:
jkummerow 2015-04-07 03:42:45 -07:00 committed by Commit bot
parent a7c1b0a92b
commit 90cbede588
16 changed files with 200 additions and 105 deletions

View 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;

View 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 =

View 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,

View 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();

View 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)");

View 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) \

View 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));
}

View 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();
}

View 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());

View 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)

View 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());

View 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

View File

@ -144,6 +144,7 @@
// - 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:

View 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();
}

View 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__);
}

View 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();