[cleanup] Unify enum cache handling.

Introduce a proper empty_descriptor_array, which has the proper layout
(length is 2 and the two fields are set properly). Also add a special
EnumCache class and a matching empty_enum_cache. The contract now is
that we only need to check the EnumLength on the map to know whether we
are allowed to use the enum cache. This greatly simplifies the handling
of the enum cache (and also the descriptor arrays), especially for the
future work on optimizing keyed access via the enum cache indices.

Bug: v8:6702
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I5ef517a3041163cd65ef003f691139ea52233e83
Reviewed-on: https://chromium-review.googlesource.com/641030
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47697}
This commit is contained in:
Benedikt Meurer 2017-08-30 06:21:29 +02:00 committed by Commit Bot
parent e84eceabbe
commit 562663d545
23 changed files with 278 additions and 294 deletions

View File

@ -4683,7 +4683,7 @@ MaybeLocal<Array> v8::Object::GetPropertyNames(Local<Context> context,
value = accumulator.GetKeys(i::GetKeysConversion::kKeepNumbers); value = accumulator.GetKeys(i::GetKeysConversion::kKeepNumbers);
DCHECK(self->map()->EnumLength() == i::kInvalidEnumCacheSentinel || DCHECK(self->map()->EnumLength() == i::kInvalidEnumCacheSentinel ||
self->map()->EnumLength() == 0 || self->map()->EnumLength() == 0 ||
self->map()->instance_descriptors()->GetEnumCache() != *value); self->map()->instance_descriptors()->GetEnumCache()->keys() != *value);
auto result = isolate->factory()->NewJSArrayWithElements(value); auto result = isolate->factory()->NewJSArrayWithElements(value);
RETURN_ESCAPED(Utils::ToLocal(result)); RETURN_ESCAPED(Utils::ToLocal(result));
} }

View File

@ -48,14 +48,12 @@ std::tuple<Node*, Node*, Node*> ForInBuiltinsAssembler::EmitForInPrepare(
BIND(&use_cache); BIND(&use_cache);
Node* map = LoadMap(object); Node* map = LoadMap(object);
Node* enum_length = EnumLength(map); Node* enum_length = EnumLength(map);
GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate);
Node* descriptors = LoadMapDescriptors(map); Node* descriptors = LoadMapDescriptors(map);
Node* cache_offset = Node* enum_cache =
LoadObjectField(descriptors, DescriptorArray::kEnumCacheBridgeOffset); LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset);
Node* enum_cache = LoadObjectField( Node* enum_keys = LoadObjectField(enum_cache, EnumCache::kKeysOffset);
cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset);
return std::make_tuple(map, enum_cache, enum_length); return std::make_tuple(map, enum_keys, enum_length);
} }
Node* ForInBuiltinsAssembler::EnumLength(Node* map) { Node* ForInBuiltinsAssembler::EnumLength(Node* map) {

View File

@ -134,12 +134,12 @@ TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) {
{ {
// The {object} has a usable enum cache, use that. // The {object} has a usable enum cache, use that.
Node* object_descriptors = LoadMapDescriptors(object_map); Node* object_descriptors = LoadMapDescriptors(object_map);
Node* object_enum_cache_bridge = LoadObjectField( Node* object_enum_cache =
object_descriptors, DescriptorArray::kEnumCacheBridgeOffset); LoadObjectField(object_descriptors, DescriptorArray::kEnumCacheOffset);
Node* object_enum_cache = LoadObjectField( Node* object_enum_keys =
object_enum_cache_bridge, DescriptorArray::kEnumCacheBridgeCacheOffset); LoadObjectField(object_enum_cache, EnumCache::kKeysOffset);
// Allocate a JSArray and copy the elements from the {object_enum_cache}. // Allocate a JSArray and copy the elements from the {object_enum_keys}.
Node* array = nullptr; Node* array = nullptr;
Node* elements = nullptr; Node* elements = nullptr;
Node* native_context = LoadNativeContext(context); Node* native_context = LoadNativeContext(context);
@ -148,7 +148,7 @@ TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) {
std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
PACKED_ELEMENTS, array_map, array_length, nullptr, object_enum_length, PACKED_ELEMENTS, array_map, array_length, nullptr, object_enum_length,
INTPTR_PARAMETERS); INTPTR_PARAMETERS);
CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_cache, elements, CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_keys, elements,
object_enum_length, SKIP_WRITE_BARRIER); object_enum_length, SKIP_WRITE_BARRIER);
Return(array); Return(array);
} }

View File

@ -509,20 +509,9 @@ FieldAccess AccessBuilder::ForFixedTypedArrayBaseExternalPointer() {
} }
// static // static
FieldAccess AccessBuilder::ForDescriptorArrayEnumCacheBridge() { FieldAccess AccessBuilder::ForDescriptorArrayEnumCache() {
FieldAccess access = { FieldAccess access = {
kTaggedBase, DescriptorArray::kEnumCacheBridgeOffset, kTaggedBase, DescriptorArray::kEnumCacheOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::OtherInternal(), MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForDescriptorArrayEnumCacheBridgeCache() {
FieldAccess access = {
kTaggedBase, DescriptorArray::kEnumCacheBridgeCacheOffset,
Handle<Name>(), MaybeHandle<Map>(), Handle<Name>(), MaybeHandle<Map>(),
Type::OtherInternal(), MachineType::TaggedPointer(), Type::OtherInternal(), MachineType::TaggedPointer(),
kPointerWriteBarrier}; kPointerWriteBarrier};
@ -940,7 +929,7 @@ ElementAccess AccessBuilder::ForFixedDoubleArrayElement() {
} }
// static // static
ElementAccess AccessBuilder::ForDescriptorArrayEnumCacheBridgeCacheElement() { ElementAccess AccessBuilder::ForEnumCacheKeysElement() {
ElementAccess access = {kTaggedBase, FixedArray::kHeaderSize, ElementAccess access = {kTaggedBase, FixedArray::kHeaderSize,
Type::InternalizedString(), Type::InternalizedString(),
MachineType::TaggedPointer(), kPointerWriteBarrier}; MachineType::TaggedPointer(), kPointerWriteBarrier};

View File

@ -169,11 +169,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to FixedTypedArrayBase::external_pointer() field. // Provides access to FixedTypedArrayBase::external_pointer() field.
static FieldAccess ForFixedTypedArrayBaseExternalPointer(); static FieldAccess ForFixedTypedArrayBaseExternalPointer();
// Provides access to DescriptorArray::enum_cache_bridge() field. // Provides access to DescriptorArray::enum_cache() field.
static FieldAccess ForDescriptorArrayEnumCacheBridge(); static FieldAccess ForDescriptorArrayEnumCache();
// Provides access to DescriptorArray::enum_cache_bridge_cache() field.
static FieldAccess ForDescriptorArrayEnumCacheBridgeCache();
// Provides access to Map::bit_field() byte. // Provides access to Map::bit_field() byte.
static FieldAccess ForMapBitField(); static FieldAccess ForMapBitField();
@ -285,8 +282,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to FixedDoubleArray elements. // Provides access to FixedDoubleArray elements.
static ElementAccess ForFixedDoubleArrayElement(); static ElementAccess ForFixedDoubleArrayElement();
// Provides access to EnumCache elements. // Provides access to EnumCache::keys elements.
static ElementAccess ForDescriptorArrayEnumCacheBridgeCacheElement(); static ElementAccess ForEnumCacheKeysElement();
// Provides access to Fixed{type}TypedArray and External{type}Array elements. // Provides access to Fixed{type}TypedArray and External{type}Array elements.
static ElementAccess ForTypedArrayElement(ExternalArrayType type, static ElementAccess ForTypedArrayElement(ExternalArrayType type,

View File

@ -270,7 +270,7 @@ Reduction JSTypeHintLowering::ReduceForInNextOperation(
receiver, cache_type, effect, control); receiver, cache_type, effect, control);
Node* node = jsgraph()->graph()->NewNode( Node* node = jsgraph()->graph()->NewNode(
jsgraph()->simplified()->LoadElement( jsgraph()->simplified()->LoadElement(
AccessBuilder::ForDescriptorArrayEnumCacheBridgeCacheElement()), AccessBuilder::ForEnumCacheKeysElement()),
cache_array, index, effect, control); cache_array, index, effect, control);
return Reduction(node); return Reduction(node);
} }

View File

@ -102,6 +102,11 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
return result; return result;
} }
Handle<EnumCache> Factory::NewEnumCache(Handle<FixedArray> keys,
Handle<FixedArray> indices) {
return Handle<EnumCache>::cast(NewTuple2(keys, indices));
}
Handle<Tuple2> Factory::NewTuple2(Handle<Object> value1, Handle<Tuple2> Factory::NewTuple2(Handle<Object> value1,
Handle<Object> value2) { Handle<Object> value2) {
Handle<Tuple2> result = Handle<Tuple2>::cast(NewStruct(TUPLE2_TYPE)); Handle<Tuple2> result = Handle<Tuple2>::cast(NewStruct(TUPLE2_TYPE));

View File

@ -126,6 +126,10 @@ class V8_EXPORT_PRIVATE Factory final {
// Create a new PrototypeInfo struct. // Create a new PrototypeInfo struct.
Handle<PrototypeInfo> NewPrototypeInfo(); Handle<PrototypeInfo> NewPrototypeInfo();
// Create a new EnumCache struct.
Handle<EnumCache> NewEnumCache(Handle<FixedArray> keys,
Handle<FixedArray> indices);
// Create a new Tuple2 struct. // Create a new Tuple2 struct.
Handle<Tuple2> NewTuple2(Handle<Object> value1, Handle<Object> value2); Handle<Tuple2> NewTuple2(Handle<Object> value1, Handle<Object> value2);

View File

@ -2426,6 +2426,11 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel, fixed_array); ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel, fixed_array);
fixed_array_map()->set_elements_kind(HOLEY_ELEMENTS); fixed_array_map()->set_elements_kind(HOLEY_ELEMENTS);
ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel,
fixed_cow_array)
fixed_cow_array_map()->set_elements_kind(HOLEY_ELEMENTS);
DCHECK_NE(fixed_array_map(), fixed_cow_array_map());
ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, undefined); ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, undefined);
ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, null); ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, null);
ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, the_hole); ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, the_hole);
@ -2464,21 +2469,48 @@ bool Heap::CreateInitialMaps() {
// Set preliminary exception sentinel value before actually initializing it. // Set preliminary exception sentinel value before actually initializing it.
set_exception(null_value()); set_exception(null_value());
// Setup the struct maps first (needed for the EnumCache).
for (unsigned i = 0; i < arraysize(struct_table); i++) {
const StructTable& entry = struct_table[i];
Map* map;
if (!AllocatePartialMap(entry.type, entry.size).To(&map)) return false;
roots_[entry.index] = map;
}
// Allocate the empty enum cache.
{
AllocationResult allocation = Allocate(tuple2_map(), OLD_SPACE);
if (!allocation.To(&obj)) return false;
}
set_empty_enum_cache(EnumCache::cast(obj));
EnumCache::cast(obj)->set_keys(empty_fixed_array());
EnumCache::cast(obj)->set_indices(empty_fixed_array());
// Allocate the empty descriptor array. // Allocate the empty descriptor array.
{ {
AllocationResult allocation = AllocateEmptyFixedArray(); AllocationResult allocation =
AllocateUninitializedFixedArray(DescriptorArray::kFirstIndex, TENURED);
if (!allocation.To(&obj)) return false; if (!allocation.To(&obj)) return false;
} }
set_empty_descriptor_array(DescriptorArray::cast(obj)); set_empty_descriptor_array(DescriptorArray::cast(obj));
DescriptorArray::cast(obj)->set(DescriptorArray::kDescriptorLengthIndex,
Smi::kZero);
DescriptorArray::cast(obj)->set(DescriptorArray::kEnumCacheIndex,
empty_enum_cache());
// Fix the instance_descriptors for the existing maps. // Fix the instance_descriptors for the existing maps.
FinalizePartialMap(this, meta_map()); FinalizePartialMap(this, meta_map());
FinalizePartialMap(this, fixed_array_map()); FinalizePartialMap(this, fixed_array_map());
FinalizePartialMap(this, fixed_cow_array_map());
FinalizePartialMap(this, undefined_map()); FinalizePartialMap(this, undefined_map());
undefined_map()->set_is_undetectable(); undefined_map()->set_is_undetectable();
FinalizePartialMap(this, null_map()); FinalizePartialMap(this, null_map());
null_map()->set_is_undetectable(); null_map()->set_is_undetectable();
FinalizePartialMap(this, the_hole_map()); FinalizePartialMap(this, the_hole_map());
for (unsigned i = 0; i < arraysize(struct_table); ++i) {
const StructTable& entry = struct_table[i];
FinalizePartialMap(this, Map::cast(roots_[entry.index]));
}
{ // Map allocation { // Map allocation
#define ALLOCATE_MAP(instance_type, size, field_name) \ #define ALLOCATE_MAP(instance_type, size, field_name) \
@ -2499,10 +2531,6 @@ bool Heap::CreateInitialMaps() {
(constructor_function_index)); \ (constructor_function_index)); \
} }
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, fixed_cow_array)
fixed_cow_array_map()->set_elements_kind(HOLEY_ELEMENTS);
DCHECK_NE(fixed_array_map(), fixed_cow_array_map());
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, scope_info) ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, scope_info)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, module_info) ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, module_info)
ALLOCATE_VARSIZE_MAP(FEEDBACK_VECTOR_TYPE, feedback_vector) ALLOCATE_VARSIZE_MAP(FEEDBACK_VECTOR_TYPE, feedback_vector)
@ -2580,13 +2608,6 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_VARSIZE_MAP(TRANSITION_ARRAY_TYPE, transition_array) ALLOCATE_VARSIZE_MAP(TRANSITION_ARRAY_TYPE, transition_array)
for (unsigned i = 0; i < arraysize(struct_table); i++) {
const StructTable& entry = struct_table[i];
Map* map;
if (!AllocateMap(entry.type, entry.size).To(&map)) return false;
roots_[entry.index] = map;
}
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, hash_table) ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, hash_table)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, ordered_hash_table) ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, ordered_hash_table)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, unseeded_number_dictionary) ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, unseeded_number_dictionary)
@ -4275,12 +4296,13 @@ AllocationResult Heap::AllocatePropertyArray(int length,
return result; return result;
} }
AllocationResult Heap::AllocateUninitializedFixedArray(int length) { AllocationResult Heap::AllocateUninitializedFixedArray(
int length, PretenureFlag pretenure) {
if (length == 0) return empty_fixed_array(); if (length == 0) return empty_fixed_array();
HeapObject* obj = nullptr; HeapObject* obj = nullptr;
{ {
AllocationResult allocation = AllocateRawFixedArray(length, NOT_TENURED); AllocationResult allocation = AllocateRawFixedArray(length, pretenure);
if (!allocation.To(&obj)) return allocation; if (!allocation.To(&obj)) return allocation;
} }

View File

@ -162,6 +162,7 @@ using v8::MemoryPressureLevel;
V(Map, optimized_out_map, OptimizedOutMap) \ V(Map, optimized_out_map, OptimizedOutMap) \
V(Map, stale_register_map, StaleRegisterMap) \ V(Map, stale_register_map, StaleRegisterMap) \
/* Canonical empty values */ \ /* Canonical empty values */ \
V(EnumCache, empty_enum_cache, EmptyEnumCache) \
V(PropertyArray, empty_property_array, EmptyPropertyArray) \ V(PropertyArray, empty_property_array, EmptyPropertyArray) \
V(ByteArray, empty_byte_array, EmptyByteArray) \ V(ByteArray, empty_byte_array, EmptyByteArray) \
V(FixedTypedArrayBase, empty_fixed_uint8_array, EmptyFixedUint8Array) \ V(FixedTypedArrayBase, empty_fixed_uint8_array, EmptyFixedUint8Array) \
@ -2082,7 +2083,8 @@ class Heap {
T t, int chars, uint32_t hash_field); T t, int chars, uint32_t hash_field);
// Allocates an uninitialized fixed array. It must be filled by the caller. // Allocates an uninitialized fixed array. It must be filled by the caller.
MUST_USE_RESULT AllocationResult AllocateUninitializedFixedArray(int length); MUST_USE_RESULT AllocationResult AllocateUninitializedFixedArray(
int length, PretenureFlag pretenure = NOT_TENURED);
// Make a copy of src and return it. // Make a copy of src and return it.
MUST_USE_RESULT inline AllocationResult CopyFixedArray(FixedArray* src); MUST_USE_RESULT inline AllocationResult CopyFixedArray(FixedArray* src);

View File

@ -2920,7 +2920,7 @@ void MarkCompactCollector::TrimDescriptorArray(Map* map,
to_trim * DescriptorArray::kEntrySize); to_trim * DescriptorArray::kEntrySize);
descriptors->SetNumberOfDescriptors(number_of_own_descriptors); descriptors->SetNumberOfDescriptors(number_of_own_descriptors);
if (descriptors->HasEnumCache()) TrimEnumCache(map, descriptors); TrimEnumCache(map, descriptors);
descriptors->Sort(); descriptors->Sort();
if (FLAG_unbox_double_fields) { if (FLAG_unbox_double_fields) {
@ -2942,16 +2942,17 @@ void MarkCompactCollector::TrimEnumCache(Map* map,
live_enum = map->NumberOfEnumerableProperties(); live_enum = map->NumberOfEnumerableProperties();
} }
if (live_enum == 0) return descriptors->ClearEnumCache(); if (live_enum == 0) return descriptors->ClearEnumCache();
EnumCache* enum_cache = descriptors->GetEnumCache();
FixedArray* enum_cache = descriptors->GetEnumCache(); FixedArray* keys = enum_cache->keys();
int to_trim = keys->length() - live_enum;
int to_trim = enum_cache->length() - live_enum;
if (to_trim <= 0) return; if (to_trim <= 0) return;
heap_->RightTrimFixedArray(descriptors->GetEnumCache(), to_trim); heap_->RightTrimFixedArray(keys, to_trim);
if (!descriptors->HasEnumIndicesCache()) return; FixedArray* indices = enum_cache->indices();
FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache(); to_trim = indices->length() - live_enum;
heap_->RightTrimFixedArray(enum_indices_cache, to_trim); if (to_trim <= 0) return;
heap_->RightTrimFixedArray(indices, to_trim);
} }

View File

@ -432,14 +432,10 @@ void ObjectStatsCollector::RecordMapDetails(Map* map_obj) {
if (map_obj->owns_descriptors() && array != heap_->empty_descriptor_array() && if (map_obj->owns_descriptors() && array != heap_->empty_descriptor_array() &&
SameLiveness(map_obj, array)) { SameLiveness(map_obj, array)) {
RecordFixedArrayHelper(map_obj, array, DESCRIPTOR_ARRAY_SUB_TYPE, 0); RecordFixedArrayHelper(map_obj, array, DESCRIPTOR_ARRAY_SUB_TYPE, 0);
if (array->HasEnumCache()) { EnumCache* enum_cache = array->GetEnumCache();
RecordFixedArrayHelper(array, array->GetEnumCache(), ENUM_CACHE_SUB_TYPE, RecordFixedArrayHelper(array, enum_cache->keys(), ENUM_CACHE_SUB_TYPE, 0);
0); RecordFixedArrayHelper(array, enum_cache->indices(),
} ENUM_INDICES_CACHE_SUB_TYPE, 0);
if (array->HasEnumIndicesCache()) {
RecordFixedArrayHelper(array, array->GetEnumIndicesCache(),
ENUM_INDICES_CACHE_SUB_TYPE, 0);
}
} }
FixedArray* code_cache = map_obj->code_cache(); FixedArray* code_cache = map_obj->code_cache();

View File

@ -259,9 +259,9 @@ void FastKeyAccumulator::Prepare() {
} }
namespace { namespace {
static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
Handle<FixedArray> array, Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
int length) { Handle<FixedArray> array, int length) {
DCHECK_LE(length, array->length()); DCHECK_LE(length, array->length());
if (array->length() == length) return array; if (array->length() == length) return array;
return isolate->factory()->CopyFixedArrayUpTo(array, length); return isolate->factory()->CopyFixedArrayUpTo(array, length);
@ -271,76 +271,77 @@ static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
// have to make sure to never directly leak the enum cache. // have to make sure to never directly leak the enum cache.
Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate, Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object) { Handle<JSObject> object) {
Handle<Map> map(object->map()); Handle<Map> map(object->map(), isolate);
bool cache_enum_length = map->OnlyHasSimpleProperties(); Handle<FixedArray> keys(map->instance_descriptors()->GetEnumCache()->keys(),
isolate);
Handle<DescriptorArray> descs = // Check if the {map} has a valid enum length, which implies that it
Handle<DescriptorArray>(map->instance_descriptors(), isolate); // must have a valid enum cache as well.
int own_property_count = map->EnumLength(); int enum_length = map->EnumLength();
// If the enum length of the given map is set to kInvalidEnumCache, this if (enum_length != kInvalidEnumCacheSentinel) {
// means that the map itself has never used the present enum cache. The DCHECK(map->OnlyHasSimpleProperties());
// first step to using the cache is to set the enum length of the map by DCHECK_LE(enum_length, keys->length());
// counting the number of own descriptors that are ENUMERABLE_STRINGS. DCHECK_EQ(enum_length, map->NumberOfEnumerableProperties());
if (own_property_count == kInvalidEnumCacheSentinel) {
own_property_count = map->NumberOfEnumerableProperties();
} else {
DCHECK_EQ(own_property_count, map->NumberOfEnumerableProperties());
}
if (descs->HasEnumCache()) {
Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
// In case the number of properties required in the enum are actually
// present, we can reuse the enum cache. Otherwise, this means that the
// enum cache was generated for a previous (smaller) version of the
// Descriptor Array. In that case we regenerate the enum cache.
if (own_property_count <= keys->length()) {
isolate->counters()->enum_cache_hits()->Increment();
if (cache_enum_length) map->SetEnumLength(own_property_count);
return ReduceFixedArrayTo(isolate, keys, own_property_count);
}
}
if (descs->IsEmpty()) {
isolate->counters()->enum_cache_hits()->Increment(); isolate->counters()->enum_cache_hits()->Increment();
if (cache_enum_length) map->SetEnumLength(0); return ReduceFixedArrayTo(isolate, keys, enum_length);
return isolate->factory()->empty_fixed_array();
} }
// Determine the actual number of enumerable properties of the {map}.
enum_length = map->NumberOfEnumerableProperties();
// Check if there's already a shared enum cache on the {map}s
// DescriptorArray with sufficient number of entries.
if (enum_length <= keys->length()) {
if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
isolate->counters()->enum_cache_hits()->Increment();
return ReduceFixedArrayTo(isolate, keys, enum_length);
}
Handle<DescriptorArray> descriptors =
Handle<DescriptorArray>(map->instance_descriptors(), isolate);
isolate->counters()->enum_cache_misses()->Increment(); isolate->counters()->enum_cache_misses()->Increment();
int nod = map->NumberOfOwnDescriptors();
Handle<FixedArray> storage = // Create the keys array.
isolate->factory()->NewFixedArray(own_property_count);
Handle<FixedArray> indices =
isolate->factory()->NewFixedArray(own_property_count);
int size = map->NumberOfOwnDescriptors();
int index = 0; int index = 0;
bool fields_only = true;
for (int i = 0; i < size; i++) { keys = isolate->factory()->NewFixedArray(enum_length);
PropertyDetails details = descs->GetDetails(i); for (int i = 0; i < nod; i++) {
DisallowHeapAllocation no_gc;
PropertyDetails details = descriptors->GetDetails(i);
if (details.IsDontEnum()) continue; if (details.IsDontEnum()) continue;
Object* key = descs->GetKey(i); Object* key = descriptors->GetKey(i);
if (key->IsSymbol()) continue; if (key->IsSymbol()) continue;
storage->set(index, key); keys->set(index, key);
if (!indices.is_null()) { if (details.location() != kField) fields_only = false;
if (details.location() == kField) {
DCHECK_EQ(kData, details.kind());
FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
int load_by_field_index = field_index.GetLoadByFieldIndex();
indices->set(index, Smi::FromInt(load_by_field_index));
} else {
indices = Handle<FixedArray>();
}
}
index++; index++;
} }
DCHECK(index == storage->length()); DCHECK_EQ(index, keys->length());
DescriptorArray::SetEnumCache(descs, isolate, storage, indices); // Optionally also create the indices array.
if (cache_enum_length) { Handle<FixedArray> indices = isolate->factory()->empty_fixed_array();
map->SetEnumLength(own_property_count); if (fields_only) {
indices = isolate->factory()->NewFixedArray(enum_length);
index = 0;
for (int i = 0; i < nod; i++) {
DisallowHeapAllocation no_gc;
PropertyDetails details = descriptors->GetDetails(i);
if (details.IsDontEnum()) continue;
Object* key = descriptors->GetKey(i);
if (key->IsSymbol()) continue;
DCHECK_EQ(kData, details.kind());
DCHECK_EQ(kField, details.location());
FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
indices->set(index, Smi::FromInt(field_index.GetLoadByFieldIndex()));
index++;
}
DCHECK_EQ(index, indices->length());
} }
return storage;
DescriptorArray::SetEnumCache(descriptors, isolate, keys, indices);
if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
return keys;
} }
template <bool fast_properties> template <bool fast_properties>

View File

@ -370,6 +370,15 @@ void JSObject::JSObjectVerify() {
field_type)); field_type));
} }
} }
if (map()->EnumLength() != kInvalidEnumCacheSentinel) {
EnumCache* enum_cache = descriptors->GetEnumCache();
FixedArray* keys = enum_cache->keys();
FixedArray* indices = enum_cache->indices();
DCHECK_LE(map()->EnumLength(), keys->length());
DCHECK_IMPLIES(indices != isolate->heap()->empty_fixed_array(),
keys->length() == indices->length());
}
} }
// If a GC was caused while constructing this object, the elements // If a GC was caused while constructing this object, the elements
@ -420,7 +429,8 @@ void Map::MapVerify() {
void Map::DictionaryMapVerify() { void Map::DictionaryMapVerify() {
MapVerify(); MapVerify();
CHECK(is_dictionary_map()); CHECK(is_dictionary_map());
CHECK(instance_descriptors()->IsEmpty()); CHECK_EQ(kInvalidEnumCacheSentinel, EnumLength());
CHECK_EQ(GetHeap()->empty_descriptor_array(), instance_descriptors());
CHECK_EQ(0, unused_property_fields()); CHECK_EQ(0, unused_property_fields());
CHECK_EQ(Map::GetVisitorId(this), visitor_id()); CHECK_EQ(Map::GetVisitorId(this), visitor_id());
} }
@ -435,6 +445,13 @@ void FixedArray::FixedArrayVerify() {
Object* e = get(i); Object* e = get(i);
VerifyPointer(e); VerifyPointer(e);
} }
Heap* heap = GetHeap();
if (this == heap->empty_descriptor_array()) {
DescriptorArray* descriptors = DescriptorArray::cast(this);
CHECK_EQ(2, length());
CHECK_EQ(0, descriptors->number_of_descriptors());
CHECK_EQ(heap->empty_enum_cache(), descriptors->GetEnumCache());
}
} }
void PropertyArray::PropertyArrayVerify() { void PropertyArray::PropertyArrayVerify() {
@ -1266,8 +1283,14 @@ void PrototypeInfo::PrototypeInfoVerify() {
void Tuple2::Tuple2Verify() { void Tuple2::Tuple2Verify() {
CHECK(IsTuple2()); CHECK(IsTuple2());
VerifyObjectField(kValue1Offset); Heap* heap = GetHeap();
VerifyObjectField(kValue2Offset); if (this == heap->empty_enum_cache()) {
CHECK_EQ(heap->empty_fixed_array(), EnumCache::cast(this)->keys());
CHECK_EQ(heap->empty_fixed_array(), EnumCache::cast(this)->indices());
} else {
VerifyObjectField(kValue1Offset);
VerifyObjectField(kValue2Offset);
}
} }
void Tuple3::Tuple3Verify() { void Tuple3::Tuple3Verify() {

View File

@ -314,6 +314,8 @@ bool HeapObject::IsJSCollection() const { return IsJSMap() || IsJSSet(); }
bool HeapObject::IsDescriptorArray() const { return IsFixedArray(); } bool HeapObject::IsDescriptorArray() const { return IsFixedArray(); }
bool HeapObject::IsEnumCache() const { return IsTuple2(); }
bool HeapObject::IsFrameArray() const { return IsFixedArray(); } bool HeapObject::IsFrameArray() const { return IsFixedArray(); }
bool HeapObject::IsArrayList() const { return IsFixedArray(); } bool HeapObject::IsArrayList() const { return IsFixedArray(); }
@ -551,6 +553,7 @@ CAST_ACCESSOR(ContextExtension)
CAST_ACCESSOR(DeoptimizationInputData) CAST_ACCESSOR(DeoptimizationInputData)
CAST_ACCESSOR(DependentCode) CAST_ACCESSOR(DependentCode)
CAST_ACCESSOR(DescriptorArray) CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(EnumCache)
CAST_ACCESSOR(FixedArray) CAST_ACCESSOR(FixedArray)
CAST_ACCESSOR(FixedArrayBase) CAST_ACCESSOR(FixedArrayBase)
CAST_ACCESSOR(FixedDoubleArray) CAST_ACCESSOR(FixedDoubleArray)
@ -2024,23 +2027,16 @@ Object** FixedArray::RawFieldOfElementAt(int index) {
return HeapObject::RawField(this, OffsetOfElementAt(index)); return HeapObject::RawField(this, OffsetOfElementAt(index));
} }
bool DescriptorArray::IsEmpty() { ACCESSORS(EnumCache, keys, FixedArray, kKeysOffset)
DCHECK(length() >= kFirstIndex || ACCESSORS(EnumCache, indices, FixedArray, kIndicesOffset)
this == GetHeap()->empty_descriptor_array());
return length() < kFirstIndex;
}
int DescriptorArray::number_of_descriptors() { int DescriptorArray::number_of_descriptors() {
DCHECK(length() >= kFirstIndex || IsEmpty()); return Smi::ToInt(get(kDescriptorLengthIndex));
int len = length();
return len == 0 ? 0 : Smi::ToInt(get(kDescriptorLengthIndex));
} }
int DescriptorArray::number_of_descriptors_storage() { int DescriptorArray::number_of_descriptors_storage() {
int len = length(); return (length() - kFirstIndex) / kEntrySize;
return len == 0 ? 0 : (len - kFirstIndex) / kEntrySize;
} }
@ -2050,8 +2046,7 @@ int DescriptorArray::NumberOfSlackDescriptors() {
void DescriptorArray::SetNumberOfDescriptors(int number_of_descriptors) { void DescriptorArray::SetNumberOfDescriptors(int number_of_descriptors) {
WRITE_FIELD( set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
this, kDescriptorLengthOffset, Smi::FromInt(number_of_descriptors));
} }
@ -2059,40 +2054,14 @@ inline int DescriptorArray::number_of_entries() {
return number_of_descriptors(); return number_of_descriptors();
} }
bool DescriptorArray::HasEnumCache() {
return !IsEmpty() && !get(kEnumCacheBridgeIndex)->IsSmi();
}
void DescriptorArray::CopyEnumCacheFrom(DescriptorArray* array) { void DescriptorArray::CopyEnumCacheFrom(DescriptorArray* array) {
set(kEnumCacheBridgeIndex, array->get(kEnumCacheBridgeIndex)); set(kEnumCacheIndex, array->get(kEnumCacheIndex));
} }
EnumCache* DescriptorArray::GetEnumCache() {
FixedArray* DescriptorArray::GetEnumCache() { return EnumCache::cast(get(kEnumCacheIndex));
DCHECK(HasEnumCache());
FixedArray* bridge = FixedArray::cast(get(kEnumCacheBridgeIndex));
return FixedArray::cast(bridge->get(kEnumCacheBridgeCacheIndex));
} }
bool DescriptorArray::HasEnumIndicesCache() {
if (IsEmpty()) return false;
Object* object = get(kEnumCacheBridgeIndex);
if (object->IsSmi()) return false;
FixedArray* bridge = FixedArray::cast(object);
return !bridge->get(kEnumCacheBridgeIndicesCacheIndex)->IsSmi();
}
FixedArray* DescriptorArray::GetEnumIndicesCache() {
DCHECK(HasEnumIndicesCache());
FixedArray* bridge = FixedArray::cast(get(kEnumCacheBridgeIndex));
return FixedArray::cast(bridge->get(kEnumCacheBridgeIndicesCacheIndex));
}
// Perform a binary search in a fixed array. // Perform a binary search in a fixed array.
template <SearchMode search_mode, typename T> template <SearchMode search_mode, typename T>
int BinarySearch(T* array, Name* name, int valid_entries, int BinarySearch(T* array, Name* name, int valid_entries,
@ -2243,7 +2212,6 @@ int Map::EnumLength() const { return EnumLengthBits::decode(bit_field3()); }
void Map::SetEnumLength(int length) { void Map::SetEnumLength(int length) {
if (length != kInvalidEnumCacheSentinel) { if (length != kInvalidEnumCacheSentinel) {
DCHECK(length >= 0); DCHECK(length >= 0);
DCHECK(length == 0 || instance_descriptors()->HasEnumCache());
DCHECK(length <= NumberOfOwnDescriptors()); DCHECK(length <= NumberOfOwnDescriptors());
} }
set_bit_field3(EnumLengthBits::update(bit_field3(), length)); set_bit_field3(EnumLengthBits::update(bit_field3(), length));

View File

@ -5045,9 +5045,7 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
// on a cache always being available once it is set. If the map has more // on a cache always being available once it is set. If the map has more
// enumerated descriptors than available in the original cache, the cache // enumerated descriptors than available in the original cache, the cache
// will be lazily replaced by the extended cache when needed. // will be lazily replaced by the extended cache when needed.
if (descriptors->HasEnumCache()) { new_descriptors->CopyEnumCacheFrom(*descriptors);
new_descriptors->CopyEnumCacheFrom(*descriptors);
}
Isolate* isolate = map->GetIsolate(); Isolate* isolate = map->GetIsolate();
// Replace descriptors by new_descriptors in all maps that share it. The old // Replace descriptors by new_descriptors in all maps that share it. The old
@ -10413,12 +10411,12 @@ Handle<DescriptorArray> DescriptorArray::Allocate(Isolate* isolate,
factory->NewFixedArray(LengthFor(size), pretenure); factory->NewFixedArray(LengthFor(size), pretenure);
result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors)); result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
result->set(kEnumCacheBridgeIndex, Smi::kZero); result->set(kEnumCacheIndex, isolate->heap()->empty_enum_cache());
return Handle<DescriptorArray>::cast(result); return Handle<DescriptorArray>::cast(result);
} }
void DescriptorArray::ClearEnumCache() { void DescriptorArray::ClearEnumCache() {
set(kEnumCacheBridgeIndex, Smi::kZero); set(kEnumCacheIndex, GetHeap()->empty_enum_cache());
} }
void DescriptorArray::Replace(int index, Descriptor* descriptor) { void DescriptorArray::Replace(int index, Descriptor* descriptor) {
@ -10426,27 +10424,17 @@ void DescriptorArray::Replace(int index, Descriptor* descriptor) {
Set(index, descriptor); Set(index, descriptor);
} }
// static // static
void DescriptorArray::SetEnumCache(Handle<DescriptorArray> descriptors, void DescriptorArray::SetEnumCache(Handle<DescriptorArray> descriptors,
Isolate* isolate, Isolate* isolate, Handle<FixedArray> keys,
Handle<FixedArray> new_cache, Handle<FixedArray> indices) {
Handle<FixedArray> new_index_cache) { EnumCache* enum_cache = descriptors->GetEnumCache();
DCHECK(!descriptors->IsEmpty()); if (enum_cache == isolate->heap()->empty_enum_cache()) {
FixedArray* bridge_storage; enum_cache = *isolate->factory()->NewEnumCache(keys, indices);
bool needs_new_enum_cache = !descriptors->HasEnumCache(); descriptors->set(kEnumCacheIndex, enum_cache);
if (needs_new_enum_cache) {
bridge_storage = *isolate->factory()->NewFixedArray(
DescriptorArray::kEnumCacheBridgeLength);
} else { } else {
bridge_storage = FixedArray::cast(descriptors->get(kEnumCacheBridgeIndex)); enum_cache->set_keys(*keys);
} enum_cache->set_indices(*indices);
bridge_storage->set(kEnumCacheBridgeCacheIndex, *new_cache);
bridge_storage->set(
kEnumCacheBridgeIndicesCacheIndex,
new_index_cache.is_null() ? Object::cast(Smi::kZero) : *new_index_cache);
if (needs_new_enum_cache) {
descriptors->set(kEnumCacheBridgeIndex, bridge_storage);
} }
} }
@ -10595,8 +10583,6 @@ int HandlerTable::LookupReturn(int pc_offset) {
#ifdef DEBUG #ifdef DEBUG
bool DescriptorArray::IsEqualTo(DescriptorArray* other) { bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
if (IsEmpty()) return other->IsEmpty();
if (other->IsEmpty()) return false;
if (length() != other->length()) return false; if (length() != other->length()) return false;
for (int i = 0; i < length(); ++i) { for (int i = 0; i < length(); ++i) {
if (get(i) != other->get(i)) return false; if (get(i) != other->get(i)) return false;

View File

@ -146,6 +146,7 @@
// - AccessCheckInfo // - AccessCheckInfo
// - InterceptorInfo // - InterceptorInfo
// - CallHandlerInfo // - CallHandlerInfo
// - EnumCache
// - TemplateInfo // - TemplateInfo
// - FunctionTemplateInfo // - FunctionTemplateInfo
// - ObjectTemplateInfo // - ObjectTemplateInfo
@ -920,6 +921,7 @@ class AllocationSite;
class Cell; class Cell;
class ConsString; class ConsString;
class ElementsAccessor; class ElementsAccessor;
class EnumCache;
class FindAndReplacePattern; class FindAndReplacePattern;
class FixedArrayBase; class FixedArrayBase;
class PropertyArray; class PropertyArray;
@ -988,6 +990,7 @@ template <class C> inline bool Is(Object* obj);
V(DeoptimizationInputData) \ V(DeoptimizationInputData) \
V(DependentCode) \ V(DependentCode) \
V(DescriptorArray) \ V(DescriptorArray) \
V(EnumCache) \
V(External) \ V(External) \
V(ExternalOneByteString) \ V(ExternalOneByteString) \
V(ExternalString) \ V(ExternalString) \

View File

@ -18,13 +18,26 @@ class Handle;
class Isolate; class Isolate;
// An EnumCache is a pair used to hold keys and indices caches.
class EnumCache : public Tuple2 {
public:
DECL_ACCESSORS(keys, FixedArray)
DECL_ACCESSORS(indices, FixedArray)
DECL_CAST(EnumCache)
// Layout description.
static const int kKeysOffset = kValue1Offset;
static const int kIndicesOffset = kValue2Offset;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(EnumCache);
};
// A DescriptorArray is a fixed array used to hold instance descriptors. // A DescriptorArray is a fixed array used to hold instance descriptors.
// The format of the these objects is: // The format of these objects is:
// [0]: Number of descriptors // [0]: Number of descriptors
// [1]: Either Smi(0) if uninitialized, // [1]: Enum cache.
// or enum cache bridge (FixedArray[2]):
// [0]: enum cache: FixedArray containing all own enumerable keys
// [1]: either Smi(0) or pointer to fixed array with indices
// [2]: first key (and internalized String) // [2]: first key (and internalized String)
// [3]: first descriptor details (see PropertyDetails) // [3]: first descriptor details (see PropertyDetails)
// [4]: first value for constants | Smi(1) when not usedA // [4]: first value for constants | Smi(1) when not usedA
@ -32,11 +45,6 @@ class Isolate;
// [2 + number of descriptors * 3]: start of slack // [2 + number of descriptors * 3]: start of slack
class DescriptorArray : public FixedArray { class DescriptorArray : public FixedArray {
public: public:
// Returns true for both shared empty_descriptor_array and for smis, which the
// map uses to encode additional bit fields when the descriptor array is not
// yet used.
inline bool IsEmpty();
// Returns the number of descriptors in the array. // Returns the number of descriptors in the array.
inline int number_of_descriptors(); inline int number_of_descriptors();
inline int number_of_descriptors_storage(); inline int number_of_descriptors_storage();
@ -45,18 +53,14 @@ class DescriptorArray : public FixedArray {
inline void SetNumberOfDescriptors(int number_of_descriptors); inline void SetNumberOfDescriptors(int number_of_descriptors);
inline int number_of_entries(); inline int number_of_entries();
inline bool HasEnumCache(); inline EnumCache* GetEnumCache();
inline bool HasEnumIndicesCache();
inline FixedArray* GetEnumCache();
inline FixedArray* GetEnumIndicesCache();
void ClearEnumCache(); void ClearEnumCache();
inline void CopyEnumCacheFrom(DescriptorArray* array); inline void CopyEnumCacheFrom(DescriptorArray* array);
// Initialize or change the enum cache, // Initialize or change the enum cache,
// using the supplied storage for the small "bridge".
static void SetEnumCache(Handle<DescriptorArray> descriptors, static void SetEnumCache(Handle<DescriptorArray> descriptors,
Isolate* isolate, Handle<FixedArray> new_cache, Isolate* isolate, Handle<FixedArray> keys,
Handle<FixedArray> new_index_cache); Handle<FixedArray> indices);
// Accessors for fetching instance descriptor at descriptor number. // Accessors for fetching instance descriptor at descriptor number.
inline Name* GetKey(int descriptor_number); inline Name* GetKey(int descriptor_number);
@ -122,22 +126,13 @@ class DescriptorArray : public FixedArray {
static const int kNotFound = -1; static const int kNotFound = -1;
static const int kDescriptorLengthIndex = 0; static const int kDescriptorLengthIndex = 0;
static const int kEnumCacheBridgeIndex = 1; static const int kEnumCacheIndex = 1;
static const int kFirstIndex = 2; static const int kFirstIndex = 2;
// The length of the "bridge" to the enum cache.
static const int kEnumCacheBridgeLength = 2;
static const int kEnumCacheBridgeCacheIndex = 0;
static const int kEnumCacheBridgeIndicesCacheIndex = 1;
// Layout description. // Layout description.
static const int kDescriptorLengthOffset = FixedArray::kHeaderSize; static const int kDescriptorLengthOffset = FixedArray::kHeaderSize;
static const int kEnumCacheBridgeOffset = static const int kEnumCacheOffset = kDescriptorLengthOffset + kPointerSize;
kDescriptorLengthOffset + kPointerSize; static const int kFirstOffset = kEnumCacheOffset + kPointerSize;
static const int kFirstOffset = kEnumCacheBridgeOffset + kPointerSize;
// Layout description for the bridge array.
static const int kEnumCacheBridgeCacheOffset = FixedArray::kHeaderSize;
// Layout of descriptor. // Layout of descriptor.
// Naming is consistent with Dictionary classes for easy templating. // Naming is consistent with Dictionary classes for easy templating.

View File

@ -126,12 +126,7 @@ RUNTIME_FUNCTION_RETURN_TRIPLE(Runtime_ForInPrepare) {
Handle<DescriptorArray> descriptors(cache_map->instance_descriptors(), Handle<DescriptorArray> descriptors(cache_map->instance_descriptors(),
isolate); isolate);
cache_length = cache_map->EnumLength(); cache_length = cache_map->EnumLength();
if (cache_length && descriptors->HasEnumCache()) { cache_array = handle(descriptors->GetEnumCache()->keys(), isolate);
cache_array = handle(descriptors->GetEnumCache(), isolate);
} else {
cache_array = isolate->factory()->empty_fixed_array();
cache_length = 0;
}
} else { } else {
cache_array = Handle<FixedArray>::cast(cache_type); cache_array = Handle<FixedArray>::cast(cache_type);
cache_length = cache_array->length(); cache_length = cache_array->length();

View File

@ -1754,7 +1754,7 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
bool transitioning = true; bool transitioning = true;
Handle<Map> map(object->map(), isolate_); Handle<Map> map(object->map(), isolate_);
DCHECK(!map->is_dictionary_map()); DCHECK(!map->is_dictionary_map());
DCHECK(map->instance_descriptors()->IsEmpty()); DCHECK_EQ(0, map->instance_descriptors()->number_of_descriptors());
std::vector<Handle<Object>> properties; std::vector<Handle<Object>> properties;
properties.reserve(8); properties.reserve(8);

View File

@ -150,7 +150,7 @@ TEST(StressJS) {
// Patch the map to have an accessor for "get". // Patch the map to have an accessor for "get".
Handle<Map> map(function->initial_map()); Handle<Map> map(function->initial_map());
Handle<DescriptorArray> instance_descriptors(map->instance_descriptors()); Handle<DescriptorArray> instance_descriptors(map->instance_descriptors());
CHECK(instance_descriptors->IsEmpty()); CHECK_EQ(0, instance_descriptors->number_of_descriptors());
PropertyAttributes attrs = NONE; PropertyAttributes attrs = NONE;
Handle<AccessorInfo> foreign = TestAccessorInfo(isolate, attrs); Handle<AccessorInfo> foreign = TestAccessorInfo(isolate, attrs);

View File

@ -2536,10 +2536,9 @@ THREADED_TEST(AccessorIsPreservedOnAttributeChange) {
LocalContext env; LocalContext env;
v8::Local<v8::Value> res = CompileRun("var a = []; a;"); v8::Local<v8::Value> res = CompileRun("var a = []; a;");
i::Handle<i::JSReceiver> a(v8::Utils::OpenHandle(v8::Object::Cast(*res))); i::Handle<i::JSReceiver> a(v8::Utils::OpenHandle(v8::Object::Cast(*res)));
CHECK(a->map()->instance_descriptors()->IsFixedArray()); CHECK_EQ(1, a->map()->instance_descriptors()->number_of_descriptors());
CHECK_GT(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0);
CompileRun("Object.defineProperty(a, 'length', { writable: false });"); CompileRun("Object.defineProperty(a, 'length', { writable: false });");
CHECK_EQ(0, i::FixedArray::cast(a->map()->instance_descriptors())->length()); CHECK_EQ(0, a->map()->instance_descriptors()->number_of_descriptors());
// But we should still have an AccessorInfo. // But we should still have an AccessorInfo.
i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length"))); i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length")));
i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR); i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR);

View File

@ -247,22 +247,22 @@ KNOWN_MAPS = {
0x03ee1: (171, "UnseededNumberDictionaryMap"), 0x03ee1: (171, "UnseededNumberDictionaryMap"),
0x03f39: (190, "ExternalMap"), 0x03f39: (190, "ExternalMap"),
0x03f91: (106, "NativeSourceStringMap"), 0x03f91: (106, "NativeSourceStringMap"),
0x03fe9: (152, "InterceptorInfoMap"), 0x03fe9: (164, "Tuple2Map"),
0x04041: (208, "JSPromiseCapabilityMap"), 0x04041: (152, "InterceptorInfoMap"),
0x04099: (149, "AccessorInfoMap"), 0x04099: (208, "JSPromiseCapabilityMap"),
0x040f1: (150, "AccessorPairMap"), 0x040f1: (149, "AccessorInfoMap"),
0x04149: (151, "AccessCheckInfoMap"), 0x04149: (150, "AccessorPairMap"),
0x041a1: (153, "FunctionTemplateInfoMap"), 0x041a1: (151, "AccessCheckInfoMap"),
0x041f9: (154, "ObjectTemplateInfoMap"), 0x041f9: (153, "FunctionTemplateInfoMap"),
0x04251: (155, "AllocationSiteMap"), 0x04251: (154, "ObjectTemplateInfoMap"),
0x042a9: (156, "AllocationMementoMap"), 0x042a9: (155, "AllocationSiteMap"),
0x04301: (158, "AliasedArgumentsEntryMap"), 0x04301: (156, "AllocationMementoMap"),
0x04359: (159, "PromiseResolveThenableJobInfoMap"), 0x04359: (158, "AliasedArgumentsEntryMap"),
0x043b1: (160, "PromiseReactionJobInfoMap"), 0x043b1: (159, "PromiseResolveThenableJobInfoMap"),
0x04409: (161, "DebugInfoMap"), 0x04409: (160, "PromiseReactionJobInfoMap"),
0x04461: (162, "StackFrameInfoMap"), 0x04461: (161, "DebugInfoMap"),
0x044b9: (163, "PrototypeInfoMap"), 0x044b9: (162, "StackFrameInfoMap"),
0x04511: (164, "Tuple2Map"), 0x04511: (163, "PrototypeInfoMap"),
0x04569: (165, "Tuple3Map"), 0x04569: (165, "Tuple3Map"),
0x045c1: (166, "ContextExtensionMap"), 0x045c1: (166, "ContextExtensionMap"),
0x04619: (167, "ModuleMap"), 0x04619: (167, "ModuleMap"),
@ -274,47 +274,47 @@ KNOWN_MAPS = {
KNOWN_OBJECTS = { KNOWN_OBJECTS = {
("OLD_SPACE", 0x02201): "NullValue", ("OLD_SPACE", 0x02201): "NullValue",
("OLD_SPACE", 0x02231): "EmptyDescriptorArray", ("OLD_SPACE", 0x02231): "EmptyDescriptorArray",
("OLD_SPACE", 0x02241): "EmptyFixedArray", ("OLD_SPACE", 0x02251): "EmptyFixedArray",
("OLD_SPACE", 0x02251): "UninitializedValue", ("OLD_SPACE", 0x02261): "UninitializedValue",
("OLD_SPACE", 0x022d1): "UndefinedValue", ("OLD_SPACE", 0x022e1): "UndefinedValue",
("OLD_SPACE", 0x02301): "NanValue", ("OLD_SPACE", 0x02311): "NanValue",
("OLD_SPACE", 0x02311): "TheHoleValue", ("OLD_SPACE", 0x02321): "TheHoleValue",
("OLD_SPACE", 0x02361): "HoleNanValue", ("OLD_SPACE", 0x02371): "HoleNanValue",
("OLD_SPACE", 0x02371): "TrueValue", ("OLD_SPACE", 0x02381): "TrueValue",
("OLD_SPACE", 0x023e1): "FalseValue", ("OLD_SPACE", 0x023f1): "FalseValue",
("OLD_SPACE", 0x02431): "empty_string", ("OLD_SPACE", 0x02441): "empty_string",
("OLD_SPACE", 0x02449): "EmptyScopeInfo", ("OLD_SPACE", 0x02459): "EmptyScopeInfo",
("OLD_SPACE", 0x02459): "ArgumentsMarker", ("OLD_SPACE", 0x02469): "ArgumentsMarker",
("OLD_SPACE", 0x024b1): "Exception", ("OLD_SPACE", 0x024c1): "Exception",
("OLD_SPACE", 0x02509): "TerminationException", ("OLD_SPACE", 0x02519): "TerminationException",
("OLD_SPACE", 0x02569): "OptimizedOut", ("OLD_SPACE", 0x02579): "OptimizedOut",
("OLD_SPACE", 0x025c1): "StaleRegister", ("OLD_SPACE", 0x025d1): "StaleRegister",
("OLD_SPACE", 0x02619): "EmptyByteArray", ("OLD_SPACE", 0x02629): "EmptyByteArray",
("OLD_SPACE", 0x02629): "EmptyFixedUint8Array", ("OLD_SPACE", 0x02639): "EmptyFixedUint8Array",
("OLD_SPACE", 0x02649): "EmptyFixedInt8Array", ("OLD_SPACE", 0x02659): "EmptyFixedInt8Array",
("OLD_SPACE", 0x02669): "EmptyFixedUint16Array", ("OLD_SPACE", 0x02679): "EmptyFixedUint16Array",
("OLD_SPACE", 0x02689): "EmptyFixedInt16Array", ("OLD_SPACE", 0x02699): "EmptyFixedInt16Array",
("OLD_SPACE", 0x026a9): "EmptyFixedUint32Array", ("OLD_SPACE", 0x026b9): "EmptyFixedUint32Array",
("OLD_SPACE", 0x026c9): "EmptyFixedInt32Array", ("OLD_SPACE", 0x026d9): "EmptyFixedInt32Array",
("OLD_SPACE", 0x026e9): "EmptyFixedFloat32Array", ("OLD_SPACE", 0x026f9): "EmptyFixedFloat32Array",
("OLD_SPACE", 0x02709): "EmptyFixedFloat64Array", ("OLD_SPACE", 0x02719): "EmptyFixedFloat64Array",
("OLD_SPACE", 0x02729): "EmptyFixedUint8ClampedArray", ("OLD_SPACE", 0x02739): "EmptyFixedUint8ClampedArray",
("OLD_SPACE", 0x02749): "EmptyScript", ("OLD_SPACE", 0x02759): "EmptyScript",
("OLD_SPACE", 0x027c9): "UndefinedCell", ("OLD_SPACE", 0x027d9): "UndefinedCell",
("OLD_SPACE", 0x027d9): "EmptySloppyArgumentsElements", ("OLD_SPACE", 0x027e9): "EmptySloppyArgumentsElements",
("OLD_SPACE", 0x027f9): "EmptySlowElementDictionary", ("OLD_SPACE", 0x02809): "EmptySlowElementDictionary",
("OLD_SPACE", 0x02841): "EmptyPropertyCell", ("OLD_SPACE", 0x02851): "EmptyPropertyCell",
("OLD_SPACE", 0x02869): "EmptyWeakCell", ("OLD_SPACE", 0x02879): "EmptyWeakCell",
("OLD_SPACE", 0x02879): "ArrayProtector", ("OLD_SPACE", 0x02889): "ArrayProtector",
("OLD_SPACE", 0x028a1): "IsConcatSpreadableProtector", ("OLD_SPACE", 0x028b1): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x028b1): "SpeciesProtector", ("OLD_SPACE", 0x028c1): "SpeciesProtector",
("OLD_SPACE", 0x028d9): "StringLengthProtector", ("OLD_SPACE", 0x028e9): "StringLengthProtector",
("OLD_SPACE", 0x028e9): "FastArrayIterationProtector", ("OLD_SPACE", 0x028f9): "FastArrayIterationProtector",
("OLD_SPACE", 0x028f9): "ArrayIteratorProtector", ("OLD_SPACE", 0x02909): "ArrayIteratorProtector",
("OLD_SPACE", 0x02921): "ArrayBufferNeuteringProtector", ("OLD_SPACE", 0x02931): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x02949): "InfinityValue", ("OLD_SPACE", 0x02959): "InfinityValue",
("OLD_SPACE", 0x02959): "MinusZeroValue", ("OLD_SPACE", 0x02969): "MinusZeroValue",
("OLD_SPACE", 0x02969): "MinusInfinityValue", ("OLD_SPACE", 0x02979): "MinusInfinityValue",
} }
# List of known V8 Frame Markers. # List of known V8 Frame Markers.