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:
parent
a7c1b0a92b
commit
90cbede588
@ -6739,7 +6739,7 @@ class Internals {
|
|||||||
static const int kNodeIsIndependentShift = 3;
|
static const int kNodeIsIndependentShift = 3;
|
||||||
static const int kNodeIsPartiallyDependentShift = 4;
|
static const int kNodeIsPartiallyDependentShift = 4;
|
||||||
|
|
||||||
static const int kJSObjectType = 0xbd;
|
static const int kJSObjectType = 0xbe;
|
||||||
static const int kFirstNonstringType = 0x80;
|
static const int kFirstNonstringType = 0x80;
|
||||||
static const int kOddballType = 0x83;
|
static const int kOddballType = 0x83;
|
||||||
static const int kForeignType = 0x87;
|
static const int kForeignType = 0x87;
|
||||||
|
@ -833,6 +833,7 @@ Handle<GlobalObject> Genesis::CreateNewGlobals(
|
|||||||
ApiNatives::GlobalObjectType);
|
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_is_hidden_prototype();
|
||||||
js_global_object_function->initial_map()->set_dictionary_map(true);
|
js_global_object_function->initial_map()->set_dictionary_map(true);
|
||||||
Handle<GlobalObject> global_object =
|
Handle<GlobalObject> global_object =
|
||||||
|
@ -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,
|
Handle<Oddball> Factory::NewOddball(Handle<Map> map,
|
||||||
const char* to_string,
|
const char* to_string,
|
||||||
Handle<Object> to_number,
|
Handle<Object> to_number,
|
||||||
|
@ -58,6 +58,9 @@ class Factory FINAL {
|
|||||||
// Create a new boxed value.
|
// Create a new boxed value.
|
||||||
Handle<Box> NewBox(Handle<Object> value);
|
Handle<Box> NewBox(Handle<Object> value);
|
||||||
|
|
||||||
|
// Create a new PrototypeInfo struct.
|
||||||
|
Handle<PrototypeInfo> NewPrototypeInfo();
|
||||||
|
|
||||||
// Create a pre-tenured empty AccessorPair.
|
// Create a pre-tenured empty AccessorPair.
|
||||||
Handle<AccessorPair> NewAccessorPair();
|
Handle<AccessorPair> NewAccessorPair();
|
||||||
|
|
||||||
|
@ -1287,9 +1287,11 @@ void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
|
|||||||
|
|
||||||
|
|
||||||
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
|
void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
|
||||||
Object* raw_transitions = map->raw_transitions();
|
Object* raw_transitions_or_prototype_info = map->raw_transitions();
|
||||||
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
|
if (TransitionArray::IsFullTransitionArray(
|
||||||
TransitionArray* transitions = TransitionArray::cast(raw_transitions);
|
raw_transitions_or_prototype_info)) {
|
||||||
|
TransitionArray* transitions =
|
||||||
|
TransitionArray::cast(raw_transitions_or_prototype_info);
|
||||||
int transitions_entry = GetEntry(transitions)->index();
|
int transitions_entry = GetEntry(transitions)->index();
|
||||||
|
|
||||||
if (FLAG_collect_maps && map->CanTransition()) {
|
if (FLAG_collect_maps && map->CanTransition()) {
|
||||||
@ -1307,11 +1309,18 @@ void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
|
|||||||
|
|
||||||
TagObject(transitions, "(transition array)");
|
TagObject(transitions, "(transition array)");
|
||||||
SetInternalReference(map, entry, "transitions", transitions,
|
SetInternalReference(map, entry, "transitions", transitions,
|
||||||
Map::kTransitionsOffset);
|
Map::kTransitionsOrPrototypeInfoOffset);
|
||||||
} else if (TransitionArray::IsSimpleTransition(raw_transitions)) {
|
} else if (TransitionArray::IsSimpleTransition(
|
||||||
TagObject(raw_transitions, "(transition)");
|
raw_transitions_or_prototype_info)) {
|
||||||
SetInternalReference(map, entry, "transition", raw_transitions,
|
TagObject(raw_transitions_or_prototype_info, "(transition)");
|
||||||
Map::kTransitionsOffset);
|
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();
|
DescriptorArray* descriptors = map->instance_descriptors();
|
||||||
TagObject(descriptors, "(map descriptors)");
|
TagObject(descriptors, "(map descriptors)");
|
||||||
|
@ -289,7 +289,6 @@ namespace internal {
|
|||||||
V(frozen_symbol) \
|
V(frozen_symbol) \
|
||||||
V(nonexistent_symbol) \
|
V(nonexistent_symbol) \
|
||||||
V(elements_transition_symbol) \
|
V(elements_transition_symbol) \
|
||||||
V(prototype_users_symbol) \
|
|
||||||
V(observed_symbol) \
|
V(observed_symbol) \
|
||||||
V(uninitialized_symbol) \
|
V(uninitialized_symbol) \
|
||||||
V(megamorphic_symbol) \
|
V(megamorphic_symbol) \
|
||||||
|
@ -585,10 +585,6 @@ template <typename StaticVisitor>
|
|||||||
void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
|
void StaticMarkingVisitor<StaticVisitor>::MarkMapContents(Heap* heap,
|
||||||
Map* map) {
|
Map* map) {
|
||||||
Object* raw_transitions = map->raw_transitions();
|
Object* raw_transitions = map->raw_transitions();
|
||||||
if (TransitionArray::IsSimpleTransition(raw_transitions)) {
|
|
||||||
StaticVisitor::VisitPointer(
|
|
||||||
heap, HeapObject::RawField(map, Map::kTransitionsOffset));
|
|
||||||
}
|
|
||||||
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
|
if (TransitionArray::IsFullTransitionArray(raw_transitions)) {
|
||||||
MarkTransitionArray(heap, TransitionArray::cast(raw_transitions));
|
MarkTransitionArray(heap, TransitionArray::cast(raw_transitions));
|
||||||
}
|
}
|
||||||
|
@ -756,14 +756,12 @@ void Isolate::ReportFailedAccessCheck(Handle<JSObject> receiver) {
|
|||||||
|
|
||||||
|
|
||||||
bool Isolate::IsInternallyUsedPropertyName(Handle<Object> name) {
|
bool Isolate::IsInternallyUsedPropertyName(Handle<Object> name) {
|
||||||
return name.is_identical_to(factory()->hidden_string()) ||
|
return name.is_identical_to(factory()->hidden_string());
|
||||||
name.is_identical_to(factory()->prototype_users_symbol());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Isolate::IsInternallyUsedPropertyName(Object* name) {
|
bool Isolate::IsInternallyUsedPropertyName(Object* name) {
|
||||||
return name == heap()->hidden_string() ||
|
return name == heap()->hidden_string();
|
||||||
name == heap()->prototype_users_symbol();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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() {
|
void AccessorInfo::AccessorInfoVerify() {
|
||||||
VerifyPointer(name());
|
VerifyPointer(name());
|
||||||
VerifyPointer(flag());
|
VerifyPointer(flag());
|
||||||
|
@ -4584,9 +4584,7 @@ void Map::set_unused_property_fields(int value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
byte Map::bit_field() {
|
byte Map::bit_field() const { return READ_BYTE_FIELD(this, kBitFieldOffset); }
|
||||||
return READ_BYTE_FIELD(this, kBitFieldOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Map::set_bit_field(byte value) {
|
void Map::set_bit_field(byte value) {
|
||||||
@ -4594,9 +4592,7 @@ void Map::set_bit_field(byte value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
byte Map::bit_field2() {
|
byte Map::bit_field2() const { return READ_BYTE_FIELD(this, kBitField2Offset); }
|
||||||
return READ_BYTE_FIELD(this, kBitField2Offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Map::set_bit_field2(byte value) {
|
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));
|
set_bit_field2(IsPrototypeMapBits::update(bit_field2(), value));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Map::is_prototype_map() {
|
bool Map::is_prototype_map() const {
|
||||||
return IsPrototypeMapBits::decode(bit_field2());
|
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);
|
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) {
|
void Map::SetBackPointer(Object* value, WriteBarrierMode mode) {
|
||||||
@ -5455,6 +5465,9 @@ ACCESSORS(ExecutableAccessorInfo, data, Object, kDataOffset)
|
|||||||
|
|
||||||
ACCESSORS(Box, value, Object, kValueOffset)
|
ACCESSORS(Box, value, Object, kValueOffset)
|
||||||
|
|
||||||
|
ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset)
|
||||||
|
ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
|
||||||
|
|
||||||
ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
|
ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
|
||||||
ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
|
ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
|
||||||
|
|
||||||
|
@ -416,7 +416,12 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
|
|||||||
os << " - unused property fields: " << unused_property_fields() << "\n";
|
os << " - unused property fields: " << unused_property_fields() << "\n";
|
||||||
if (is_deprecated()) os << " - deprecated_map\n";
|
if (is_deprecated()) os << " - deprecated_map\n";
|
||||||
if (is_dictionary_map()) os << " - dictionary_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 (is_hidden_prototype()) os << " - hidden_prototype\n";
|
||||||
if (has_named_interceptor()) os << " - named_interceptor\n";
|
if (has_named_interceptor()) os << " - named_interceptor\n";
|
||||||
if (has_indexed_interceptor()) os << " - indexed_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_access_check_needed()) os << " - access_check_needed\n";
|
||||||
if (!is_extensible()) os << " - non-extensible\n";
|
if (!is_extensible()) os << " - non-extensible\n";
|
||||||
if (is_observed()) os << " - observed\n";
|
if (is_observed()) os << " - observed\n";
|
||||||
os << " - back pointer: " << Brief(GetBackPointer());
|
|
||||||
os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
|
os << "\n - instance descriptors " << (owns_descriptors() ? "(own) " : "")
|
||||||
<< "#" << NumberOfOwnDescriptors() << ": "
|
<< "#" << NumberOfOwnDescriptors() << ": "
|
||||||
<< Brief(instance_descriptors());
|
<< 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
|
void AccessorPair::AccessorPairPrint(std::ostream& os) { // NOLINT
|
||||||
HeapObject::PrintHeader(os, "AccessorPair");
|
HeapObject::PrintHeader(os, "AccessorPair");
|
||||||
os << "\n - getter: " << Brief(getter());
|
os << "\n - getter: " << Brief(getter());
|
||||||
|
@ -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;
|
if (object->map() == *new_map) return;
|
||||||
|
Handle<Map> old_map(object->map());
|
||||||
if (object->HasFastProperties()) {
|
if (object->HasFastProperties()) {
|
||||||
if (!new_map->is_dictionary_map()) {
|
if (!new_map->is_dictionary_map()) {
|
||||||
Handle<Map> old_map(object->map());
|
|
||||||
MigrateFastToFast(object, new_map);
|
MigrateFastToFast(object, new_map);
|
||||||
if (old_map->is_prototype_map()) {
|
if (old_map->is_prototype_map()) {
|
||||||
// Clear out the old descriptor array to avoid problems to sharing
|
// 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());
|
DCHECK(new_map->GetBackPointer()->IsUndefined());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MigrateFastToSlow(object, new_map, 0);
|
MigrateFastToSlow(object, new_map, expected_additional_properties);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For slow-to-fast migrations JSObject::TransformToFastProperties()
|
// For slow-to-fast migrations JSObject::MigrateSlowToFast()
|
||||||
// must be used instead.
|
// must be used instead.
|
||||||
CHECK(new_map->is_dictionary_map());
|
CHECK(new_map->is_dictionary_map());
|
||||||
|
|
||||||
// Slow-to-slow migration is trivial.
|
// Slow-to-slow migration is trivial.
|
||||||
object->set_map(*new_map);
|
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> map(object->map());
|
||||||
Handle<Map> new_map = Map::Normalize(map, mode, reason);
|
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,
|
void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
|
||||||
PrototypeOptimizationMode mode) {
|
PrototypeOptimizationMode mode) {
|
||||||
if (object->IsGlobalObject()) return;
|
if (object->IsGlobalObject()) return;
|
||||||
@ -9955,14 +9961,12 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
|
|||||||
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
|
JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0,
|
||||||
"NormalizeAsPrototype");
|
"NormalizeAsPrototype");
|
||||||
}
|
}
|
||||||
bool has_just_copied_map = false;
|
Handle<Map> previous_map(object->map());
|
||||||
if (!object->HasFastProperties()) {
|
if (!object->HasFastProperties()) {
|
||||||
JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
|
JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype");
|
||||||
has_just_copied_map = true;
|
|
||||||
}
|
}
|
||||||
if (mode == FAST_PROTOTYPE && object->HasFastProperties() &&
|
if (!object->map()->is_prototype_map()) {
|
||||||
!object->map()->is_prototype_map()) {
|
if (object->map() == *previous_map) {
|
||||||
if (!has_just_copied_map) {
|
|
||||||
Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
|
Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype");
|
||||||
JSObject::MigrateToMap(object, new_map);
|
JSObject::MigrateToMap(object, new_map);
|
||||||
}
|
}
|
||||||
@ -9985,39 +9989,54 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
|
void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
|
||||||
if (!object->map()->is_prototype_map()) return;
|
if (!object->map()->is_prototype_map()) return;
|
||||||
OptimizeAsPrototype(object, FAST_PROTOTYPE);
|
OptimizeAsPrototype(object, FAST_PROTOTYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
void JSObject::RegisterPrototypeUser(Handle<JSObject> prototype,
|
void JSObject::RegisterPrototypeUser(Handle<JSObject> prototype,
|
||||||
Handle<HeapObject> user) {
|
Handle<HeapObject> user) {
|
||||||
DCHECK(FLAG_track_prototype_users);
|
DCHECK(FLAG_track_prototype_users);
|
||||||
Isolate* isolate = prototype->GetIsolate();
|
Isolate* isolate = prototype->GetIsolate();
|
||||||
Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
|
if (prototype->IsJSGlobalProxy()) {
|
||||||
|
PrototypeIterator iter(isolate, prototype);
|
||||||
// Get prototype users array, create it if it doesn't exist yet.
|
prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
|
||||||
Handle<Object> maybe_array =
|
}
|
||||||
JSObject::GetProperty(prototype, symbol).ToHandleChecked();
|
Handle<PrototypeInfo> proto_info;
|
||||||
|
Object* maybe_proto_info = prototype->map()->prototype_info();
|
||||||
Handle<WeakFixedArray> new_array = WeakFixedArray::Add(maybe_array, user);
|
if (maybe_proto_info->IsPrototypeInfo()) {
|
||||||
if (!maybe_array.is_identical_to(new_array)) {
|
proto_info = handle(PrototypeInfo::cast(maybe_proto_info), isolate);
|
||||||
JSObject::SetOwnPropertyIgnoreAttributes(prototype, symbol, new_array,
|
} else {
|
||||||
DONT_ENUM).Assert();
|
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,
|
void JSObject::UnregisterPrototypeUser(Handle<JSObject> prototype,
|
||||||
Handle<HeapObject> user) {
|
Handle<HeapObject> user) {
|
||||||
Isolate* isolate = prototype->GetIsolate();
|
Isolate* isolate = prototype->GetIsolate();
|
||||||
Handle<Name> symbol = isolate->factory()->prototype_users_symbol();
|
if (prototype->IsJSGlobalProxy()) {
|
||||||
|
PrototypeIterator iter(isolate, prototype);
|
||||||
Handle<Object> maybe_array =
|
prototype = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
|
||||||
JSObject::GetProperty(prototype, symbol).ToHandleChecked();
|
}
|
||||||
if (!maybe_array->IsWeakFixedArray()) return;
|
DCHECK(prototype->map()->is_prototype_map());
|
||||||
Handle<WeakFixedArray>::cast(maybe_array)->Remove(user);
|
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) {
|
bool Map::ShouldRegisterAsPrototypeUser(Handle<JSObject> prototype) {
|
||||||
if (!FLAG_track_prototype_users) return false;
|
if (!FLAG_track_prototype_users) return false;
|
||||||
if (this->is_prototype_map()) return true;
|
if (this->is_prototype_map()) return true;
|
||||||
if (this->is_dictionary_map()) return false;
|
|
||||||
Object* back = GetBackPointer();
|
Object* back = GetBackPointer();
|
||||||
if (!back->IsMap()) return true;
|
if (!back->IsMap()) return true;
|
||||||
if (Map::cast(back)->prototype() != *prototype) 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<Object> CacheInitialJSArrayMaps(
|
||||||
Handle<Context> native_context, Handle<Map> initial_map) {
|
Handle<Context> native_context, Handle<Map> initial_map) {
|
||||||
// Replace all of the cached initial array maps in the native context with
|
// Replace all of the cached initial array maps in the native context with
|
||||||
|
@ -144,6 +144,7 @@
|
|||||||
// - DebugInfo
|
// - DebugInfo
|
||||||
// - BreakPointInfo
|
// - BreakPointInfo
|
||||||
// - CodeCache
|
// - CodeCache
|
||||||
|
// - PrototypeInfo
|
||||||
// - WeakCell
|
// - WeakCell
|
||||||
//
|
//
|
||||||
// Formats of Object*:
|
// Formats of Object*:
|
||||||
@ -420,6 +421,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
|
|||||||
V(TYPE_FEEDBACK_INFO_TYPE) \
|
V(TYPE_FEEDBACK_INFO_TYPE) \
|
||||||
V(ALIASED_ARGUMENTS_ENTRY_TYPE) \
|
V(ALIASED_ARGUMENTS_ENTRY_TYPE) \
|
||||||
V(BOX_TYPE) \
|
V(BOX_TYPE) \
|
||||||
|
V(PROTOTYPE_INFO_TYPE) \
|
||||||
\
|
\
|
||||||
V(FIXED_ARRAY_TYPE) \
|
V(FIXED_ARRAY_TYPE) \
|
||||||
V(FIXED_DOUBLE_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
|
// 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
|
// type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST
|
||||||
// manually.
|
// manually.
|
||||||
#define STRUCT_LIST(V) \
|
#define STRUCT_LIST(V) \
|
||||||
V(BOX, Box, box) \
|
V(BOX, Box, box) \
|
||||||
V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo, executable_accessor_info)\
|
V(EXECUTABLE_ACCESSOR_INFO, ExecutableAccessorInfo, \
|
||||||
V(ACCESSOR_PAIR, AccessorPair, accessor_pair) \
|
executable_accessor_info) \
|
||||||
V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
|
V(ACCESSOR_PAIR, AccessorPair, accessor_pair) \
|
||||||
V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
|
V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
|
||||||
V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \
|
V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
|
||||||
V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
|
V(CALL_HANDLER_INFO, CallHandlerInfo, call_handler_info) \
|
||||||
V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
|
V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
|
||||||
V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
|
V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
|
||||||
V(SCRIPT, Script, script) \
|
V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
|
||||||
V(ALLOCATION_SITE, AllocationSite, allocation_site) \
|
V(SCRIPT, Script, script) \
|
||||||
V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento) \
|
V(ALLOCATION_SITE, AllocationSite, allocation_site) \
|
||||||
V(CODE_CACHE, CodeCache, code_cache) \
|
V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento) \
|
||||||
V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \
|
V(CODE_CACHE, CodeCache, code_cache) \
|
||||||
V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
|
V(POLYMORPHIC_CODE_CACHE, PolymorphicCodeCache, polymorphic_code_cache) \
|
||||||
V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
|
V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
|
||||||
V(DEBUG_INFO, DebugInfo, debug_info) \
|
V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
|
||||||
V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)
|
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
|
// 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
|
// 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,
|
SHARED_FUNCTION_INFO_TYPE,
|
||||||
WEAK_CELL_TYPE,
|
WEAK_CELL_TYPE,
|
||||||
PROPERTY_CELL_TYPE,
|
PROPERTY_CELL_TYPE,
|
||||||
|
PROTOTYPE_INFO_TYPE,
|
||||||
|
|
||||||
// All the following types are subtypes of JSReceiver, which corresponds to
|
// 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
|
// 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,
|
static void TransitionElementsKind(Handle<JSObject> object,
|
||||||
ElementsKind to_kind);
|
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
|
// Convert the object to use the canonical dictionary
|
||||||
// representation. If the object is expected to have additional properties
|
// 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 Object* Get(int index) const;
|
||||||
inline int Length() const;
|
inline int Length() const;
|
||||||
|
|
||||||
|
static Object* Empty() { return Smi::FromInt(0); }
|
||||||
|
|
||||||
DECLARE_CAST(WeakFixedArray)
|
DECLARE_CAST(WeakFixedArray)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -5839,15 +5850,15 @@ class Map: public HeapObject {
|
|||||||
inline void set_unused_property_fields(int value);
|
inline void set_unused_property_fields(int value);
|
||||||
|
|
||||||
// Bit field.
|
// Bit field.
|
||||||
inline byte bit_field();
|
inline byte bit_field() const;
|
||||||
inline void set_bit_field(byte value);
|
inline void set_bit_field(byte value);
|
||||||
|
|
||||||
// Bit field 2.
|
// Bit field 2.
|
||||||
inline byte bit_field2();
|
inline byte bit_field2() const;
|
||||||
inline void set_bit_field2(byte value);
|
inline void set_bit_field2(byte value);
|
||||||
|
|
||||||
// Bit field 3.
|
// Bit field 3.
|
||||||
inline uint32_t bit_field3();
|
inline uint32_t bit_field3() const;
|
||||||
inline void set_bit_field3(uint32_t bits);
|
inline void set_bit_field3(uint32_t bits);
|
||||||
|
|
||||||
class EnumLengthBits: public BitField<int,
|
class EnumLengthBits: public BitField<int,
|
||||||
@ -5942,7 +5953,7 @@ class Map: public HeapObject {
|
|||||||
inline void set_is_extensible(bool value);
|
inline void set_is_extensible(bool value);
|
||||||
inline bool is_extensible();
|
inline bool is_extensible();
|
||||||
inline void set_is_prototype_map(bool value);
|
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) {
|
inline void set_elements_kind(ElementsKind elements_kind) {
|
||||||
DCHECK(static_cast<int>(elements_kind) < kElementsKindCount);
|
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
|
// Don't call set_raw_transitions() directly to overwrite transitions, use
|
||||||
// the TransitionArray::ReplaceTransitions() wrapper instead!
|
// the TransitionArray::ReplaceTransitions() wrapper instead!
|
||||||
DECL_ACCESSORS(raw_transitions, Object)
|
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* FindRootMap();
|
||||||
Map* FindFieldOwner(int descriptor);
|
Map* FindFieldOwner(int descriptor);
|
||||||
@ -6371,9 +6385,10 @@ class Map: public HeapObject {
|
|||||||
// otherwise a transition array is used.
|
// otherwise a transition array is used.
|
||||||
// For prototype maps, this slot is used to store a pointer to the prototype
|
// For prototype maps, this slot is used to store a pointer to the prototype
|
||||||
// object using this map.
|
// object using this map.
|
||||||
static const int kTransitionsOffset =
|
static const int kTransitionsOrPrototypeInfoOffset =
|
||||||
kConstructorOrBackPointerOffset + kPointerSize;
|
kConstructorOrBackPointerOffset + kPointerSize;
|
||||||
static const int kDescriptorsOffset = kTransitionsOffset + kPointerSize;
|
static const int kDescriptorsOffset =
|
||||||
|
kTransitionsOrPrototypeInfoOffset + kPointerSize;
|
||||||
#if V8_DOUBLE_FIELDS_UNBOXING
|
#if V8_DOUBLE_FIELDS_UNBOXING
|
||||||
static const int kLayoutDecriptorOffset = kDescriptorsOffset + kPointerSize;
|
static const int kLayoutDecriptorOffset = kDescriptorsOffset + kPointerSize;
|
||||||
static const int kCodeCacheOffset = kLayoutDecriptorOffset + 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.
|
// Script describes a script which has been added to the VM.
|
||||||
class Script: public Struct {
|
class Script: public Struct {
|
||||||
public:
|
public:
|
||||||
|
@ -316,6 +316,8 @@ void TransitionArray::SetNumberOfPrototypeTransitions(
|
|||||||
int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
|
int TransitionArray::NumberOfTransitions(Object* raw_transitions) {
|
||||||
if (CanStoreSimpleTransition(raw_transitions)) return 0;
|
if (CanStoreSimpleTransition(raw_transitions)) return 0;
|
||||||
if (IsSimpleTransition(raw_transitions)) return 1;
|
if (IsSimpleTransition(raw_transitions)) return 1;
|
||||||
|
// Prototype maps don't have transitions.
|
||||||
|
if (raw_transitions->IsPrototypeInfo()) return 0;
|
||||||
DCHECK(IsFullTransitionArray(raw_transitions));
|
DCHECK(IsFullTransitionArray(raw_transitions));
|
||||||
return TransitionArray::cast(raw_transitions)->number_of_transitions();
|
return TransitionArray::cast(raw_transitions)->number_of_transitions();
|
||||||
}
|
}
|
||||||
|
@ -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() : {};
|
var proto = use_new ? new Super() : {};
|
||||||
|
|
||||||
// New object is fast.
|
// New object is fast.
|
||||||
@ -74,16 +74,8 @@ function test(use_new, add_first, set__proto__, same_map_as) {
|
|||||||
// Still fast
|
// Still fast
|
||||||
assertTrue(%HasFastProperties(proto));
|
assertTrue(%HasFastProperties(proto));
|
||||||
AddProps(proto);
|
AddProps(proto);
|
||||||
if (set__proto__) {
|
// Still fast.
|
||||||
// After we add all those properties it went slow mode again :-(
|
assertTrue(%HasFastProperties(proto));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
@ -96,9 +88,7 @@ for (var i = 0; i < 4; i++) {
|
|||||||
var use_new = ((i & 2) != 0);
|
var use_new = ((i & 2) != 0);
|
||||||
|
|
||||||
test(use_new, true, set__proto__);
|
test(use_new, true, set__proto__);
|
||||||
|
test(use_new, false, set__proto__);
|
||||||
var last = test(use_new, false, set__proto__);
|
|
||||||
test(use_new, false, set__proto__, last);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ function __f_1(__v_4, add_first, __v_6, same_map_as) {
|
|||||||
__f_0(__v_1, __v_6);
|
__f_0(__v_1, __v_6);
|
||||||
assertTrue(%HasFastProperties(__v_1));
|
assertTrue(%HasFastProperties(__v_1));
|
||||||
__f_4(__v_1);
|
__f_4(__v_1);
|
||||||
assertFalse(%HasFastProperties(__v_1));
|
assertTrue(%HasFastProperties(__v_1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gc();
|
gc();
|
||||||
|
Loading…
Reference in New Issue
Block a user