[dict-proto] Add support for ordered property dicts, pt.1
This CL adds partial support for objects whose slow mode dictionaries are OrderedNameDictionaries. This is the case for all slow mode objects if V8_DICT_MODE_PROTOTYPES is enabled. Bug: v8:7569 Change-Id: I0b5a0d751e6551e78121569ddefd9e00c164cc5a Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2489692 Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Frank Emrich <emrich@google.com> Cr-Commit-Position: refs/heads/master@{#70952}
This commit is contained in:
parent
5326c4f86e
commit
4b6455aab6
@ -3371,7 +3371,7 @@ TNode<NameDictionary> CodeStubAssembler::CopyNameDictionary(
|
||||
|
||||
template <typename CollectionType>
|
||||
TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTable() {
|
||||
static const int kCapacity = CollectionType::kMinNonZeroCapacity;
|
||||
static const int kCapacity = CollectionType::kInitialCapacity;
|
||||
static const int kBucketCount = kCapacity / CollectionType::kLoadFactor;
|
||||
static const int kDataTableLength = kCapacity * CollectionType::kEntrySize;
|
||||
static const int kFixedArrayLength =
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
#include "src/common/globals.h"
|
||||
#include "src/compiler/node.h"
|
||||
#include "src/diagnostics/disasm.h"
|
||||
#include "src/diagnostics/disassembler.h"
|
||||
@ -274,6 +275,8 @@ bool JSObject::PrintProperties(std::ostream& os) { // NOLINT
|
||||
return map().NumberOfOwnDescriptors() > 0;
|
||||
} else if (IsJSGlobalObject()) {
|
||||
JSGlobalObject::cast(*this).global_dictionary().Print(os);
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
property_dictionary_ordered().Print(os);
|
||||
} else {
|
||||
property_dictionary().Print(os);
|
||||
}
|
||||
|
@ -520,23 +520,27 @@ Handle<SmallOrderedNameDictionary> Factory::NewSmallOrderedNameDictionary(
|
||||
}
|
||||
|
||||
Handle<OrderedHashSet> Factory::NewOrderedHashSet() {
|
||||
return OrderedHashSet::Allocate(isolate(),
|
||||
OrderedHashSet::kMinNonZeroCapacity)
|
||||
return OrderedHashSet::Allocate(isolate(), OrderedHashSet::kInitialCapacity,
|
||||
AllocationType::kYoung)
|
||||
.ToHandleChecked();
|
||||
}
|
||||
|
||||
Handle<OrderedHashMap> Factory::NewOrderedHashMap() {
|
||||
return OrderedHashMap::Allocate(isolate(),
|
||||
OrderedHashMap::kMinNonZeroCapacity)
|
||||
return OrderedHashMap::Allocate(isolate(), OrderedHashMap::kInitialCapacity,
|
||||
AllocationType::kYoung)
|
||||
.ToHandleChecked();
|
||||
}
|
||||
|
||||
Handle<OrderedNameDictionary> Factory::NewOrderedNameDictionary() {
|
||||
return OrderedNameDictionary::Allocate(
|
||||
isolate(), OrderedNameDictionary::kMinNonZeroCapacity)
|
||||
Handle<OrderedNameDictionary> Factory::NewOrderedNameDictionary(int capacity) {
|
||||
return OrderedNameDictionary::Allocate(isolate(), capacity,
|
||||
AllocationType::kYoung)
|
||||
.ToHandleChecked();
|
||||
}
|
||||
|
||||
Handle<NameDictionary> Factory::NewNameDictionary(int at_least_space_for) {
|
||||
return NameDictionary::New(isolate(), at_least_space_for);
|
||||
}
|
||||
|
||||
Handle<PropertyDescriptorObject> Factory::NewPropertyDescriptorObject() {
|
||||
Handle<PropertyDescriptorObject> object =
|
||||
Handle<PropertyDescriptorObject>::cast(
|
||||
|
@ -164,10 +164,17 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
|
||||
|
||||
Handle<FrameArray> NewFrameArray(int number_of_frames);
|
||||
|
||||
// Allocates a |NameDictionary| with an internal capacity calculated such that
|
||||
// |at_least_space_for| entries can be added without reallocating.
|
||||
Handle<NameDictionary> NewNameDictionary(int at_least_space_for);
|
||||
|
||||
// Allocates an |OrderedNameDictionary| of the given capacity. This guarantees
|
||||
// that |capacity| entries can be added without reallocating.
|
||||
Handle<OrderedNameDictionary> NewOrderedNameDictionary(
|
||||
int capacity = OrderedNameDictionary::kInitialCapacity);
|
||||
|
||||
Handle<OrderedHashSet> NewOrderedHashSet();
|
||||
Handle<OrderedHashMap> NewOrderedHashMap();
|
||||
Handle<OrderedNameDictionary> NewOrderedNameDictionary();
|
||||
|
||||
Handle<SmallOrderedHashSet> NewSmallOrderedHashSet(
|
||||
int capacity = kSmallOrderedHashSetMinCapacity,
|
||||
AllocationType allocation = AllocationType::kYoung);
|
||||
|
@ -131,6 +131,8 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary
|
||||
static const int kObjectHashIndex = kNextEnumerationIndexIndex + 1;
|
||||
static const int kEntryValueIndex = 1;
|
||||
|
||||
static const bool kIsOrderedDictionaryType = false;
|
||||
|
||||
inline void SetHash(int hash);
|
||||
inline int Hash() const;
|
||||
|
||||
|
@ -640,9 +640,15 @@ void JSReceiver::initialize_properties(Isolate* isolate) {
|
||||
ReadOnlyRoots roots(isolate);
|
||||
DCHECK(!ObjectInYoungGeneration(roots.empty_fixed_array()));
|
||||
DCHECK(!ObjectInYoungGeneration(roots.empty_property_dictionary()));
|
||||
DCHECK(!ObjectInYoungGeneration(roots.empty_ordered_property_dictionary()));
|
||||
if (map(isolate).is_dictionary_map()) {
|
||||
WRITE_FIELD(*this, kPropertiesOrHashOffset,
|
||||
roots.empty_property_dictionary());
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
WRITE_FIELD(*this, kPropertiesOrHashOffset,
|
||||
roots.empty_ordered_property_dictionary());
|
||||
} else {
|
||||
WRITE_FIELD(*this, kPropertiesOrHashOffset,
|
||||
roots.empty_property_dictionary());
|
||||
}
|
||||
} else {
|
||||
WRITE_FIELD(*this, kPropertiesOrHashOffset, roots.empty_fixed_array());
|
||||
}
|
||||
@ -651,7 +657,8 @@ void JSReceiver::initialize_properties(Isolate* isolate) {
|
||||
DEF_GETTER(JSReceiver, HasFastProperties, bool) {
|
||||
DCHECK(raw_properties_or_hash(isolate).IsSmi() ||
|
||||
((raw_properties_or_hash(isolate).IsGlobalDictionary(isolate) ||
|
||||
raw_properties_or_hash(isolate).IsNameDictionary(isolate)) ==
|
||||
raw_properties_or_hash(isolate).IsNameDictionary(isolate) ||
|
||||
raw_properties_or_hash(isolate).IsOrderedNameDictionary(isolate)) ==
|
||||
map(isolate).is_dictionary_map()));
|
||||
return !map(isolate).is_dictionary_map();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "src/objects/js-objects.h"
|
||||
|
||||
#include "src/api/api-arguments-inl.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/date/date.h"
|
||||
#include "src/execution/arguments.h"
|
||||
#include "src/execution/frames.h"
|
||||
@ -323,12 +324,18 @@ Maybe<bool> JSReceiver::SetOrCopyDataProperties(
|
||||
DCHECK(!target->IsJSProxy());
|
||||
// Convert to slow properties if we're guaranteed to overflow the number of
|
||||
// descriptors.
|
||||
int source_length =
|
||||
from->IsJSGlobalObject()
|
||||
? JSGlobalObject::cast(*from)
|
||||
.global_dictionary()
|
||||
.NumberOfEnumerableProperties()
|
||||
: from->property_dictionary().NumberOfEnumerableProperties();
|
||||
int source_length;
|
||||
if (from->IsJSGlobalObject()) {
|
||||
source_length = JSGlobalObject::cast(*from)
|
||||
.global_dictionary()
|
||||
.NumberOfEnumerableProperties();
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
source_length =
|
||||
from->property_dictionary_ordered().NumberOfEnumerableProperties();
|
||||
} else {
|
||||
source_length =
|
||||
from->property_dictionary().NumberOfEnumerableProperties();
|
||||
}
|
||||
if (source_length > kMaxNumberOfDescriptors) {
|
||||
JSObject::NormalizeProperties(isolate, Handle<JSObject>::cast(target),
|
||||
CLEAR_INOBJECT_PROPERTIES, source_length,
|
||||
@ -608,7 +615,8 @@ Object SetHashAndUpdateProperties(HeapObject properties, int hash) {
|
||||
ReadOnlyRoots roots = properties.GetReadOnlyRoots();
|
||||
if (properties == roots.empty_fixed_array() ||
|
||||
properties == roots.empty_property_array() ||
|
||||
properties == roots.empty_property_dictionary()) {
|
||||
properties == roots.empty_property_dictionary() ||
|
||||
properties == roots.empty_ordered_property_dictionary()) {
|
||||
return Smi::FromInt(hash);
|
||||
}
|
||||
|
||||
@ -623,8 +631,13 @@ Object SetHashAndUpdateProperties(HeapObject properties, int hash) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
DCHECK(properties.IsNameDictionary());
|
||||
NameDictionary::cast(properties).SetHash(hash);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
DCHECK(properties.IsOrderedNameDictionary());
|
||||
OrderedNameDictionary::cast(properties).SetHash(hash);
|
||||
} else {
|
||||
DCHECK(properties.IsNameDictionary());
|
||||
NameDictionary::cast(properties).SetHash(hash);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
@ -638,8 +651,12 @@ int GetIdentityHashHelper(JSReceiver object) {
|
||||
if (properties.IsPropertyArray()) {
|
||||
return PropertyArray::cast(properties).Hash();
|
||||
}
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL && properties.IsOrderedNameDictionary()) {
|
||||
return OrderedNameDictionary::cast(properties).Hash();
|
||||
}
|
||||
|
||||
if (properties.IsNameDictionary()) {
|
||||
DCHECK(!V8_DICT_MODE_PROTOTYPES_BOOL);
|
||||
return NameDictionary::cast(properties).Hash();
|
||||
}
|
||||
|
||||
@ -650,7 +667,8 @@ int GetIdentityHashHelper(JSReceiver object) {
|
||||
#ifdef DEBUG
|
||||
ReadOnlyRoots roots = object.GetReadOnlyRoots();
|
||||
DCHECK(properties == roots.empty_fixed_array() ||
|
||||
properties == roots.empty_property_dictionary());
|
||||
properties == roots.empty_property_dictionary() ||
|
||||
properties == roots.empty_ordered_property_dictionary());
|
||||
#endif
|
||||
|
||||
return PropertyArray::kNoHashSentinel;
|
||||
@ -736,10 +754,19 @@ void JSReceiver::DeleteNormalizedProperty(Handle<JSReceiver> object,
|
||||
|
||||
cell->ClearAndInvalidate(ReadOnlyRoots(isolate));
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
Handle<OrderedNameDictionary> dictionary(
|
||||
object->property_dictionary_ordered(), isolate);
|
||||
|
||||
dictionary = NameDictionary::DeleteEntry(isolate, dictionary, entry);
|
||||
object->SetProperties(*dictionary);
|
||||
dictionary =
|
||||
OrderedNameDictionary::DeleteEntry(isolate, dictionary, entry);
|
||||
object->SetProperties(*dictionary);
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
|
||||
|
||||
dictionary = NameDictionary::DeleteEntry(isolate, dictionary, entry);
|
||||
object->SetProperties(*dictionary);
|
||||
}
|
||||
}
|
||||
if (object->map().is_prototype_map()) {
|
||||
// Invalidate prototype validity cell as this may invalidate transitioning
|
||||
@ -2075,9 +2102,11 @@ MaybeHandle<JSObject> JSObject::New(Handle<JSFunction> constructor,
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, initial_map,
|
||||
JSFunction::GetDerivedMap(isolate, constructor, new_target), JSObject);
|
||||
int initial_capacity = V8_DICT_MODE_PROTOTYPES_BOOL
|
||||
? OrderedNameDictionary::kInitialCapacity
|
||||
: NameDictionary::kInitialCapacity;
|
||||
Handle<JSObject> result = isolate->factory()->NewFastOrSlowJSObjectFromMap(
|
||||
initial_map, NameDictionary::kInitialCapacity, AllocationType::kYoung,
|
||||
site);
|
||||
initial_map, initial_capacity, AllocationType::kYoung, site);
|
||||
isolate->counters()->constructed_objects()->Increment();
|
||||
isolate->counters()->constructed_objects_runtime()->Increment();
|
||||
return result;
|
||||
@ -2386,21 +2415,36 @@ void JSObject::SetNormalizedProperty(Handle<JSObject> object, Handle<Name> name,
|
||||
cell->set_value(*value);
|
||||
}
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
|
||||
|
||||
InternalIndex entry = dictionary->FindEntry(isolate, name);
|
||||
if (entry.is_not_found()) {
|
||||
DCHECK_IMPLIES(object->map().is_prototype_map(),
|
||||
Map::IsPrototypeChainInvalidated(object->map()));
|
||||
dictionary =
|
||||
NameDictionary::Add(isolate, dictionary, name, value, details);
|
||||
object->SetProperties(*dictionary);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
Handle<OrderedNameDictionary> dictionary(
|
||||
object->property_dictionary_ordered(), isolate);
|
||||
InternalIndex entry = dictionary->FindEntry(isolate, *name);
|
||||
if (entry.is_not_found()) {
|
||||
DCHECK_IMPLIES(object->map().is_prototype_map(),
|
||||
Map::IsPrototypeChainInvalidated(object->map()));
|
||||
dictionary = OrderedNameDictionary::Add(isolate, dictionary, name,
|
||||
value, details)
|
||||
.ToHandleChecked();
|
||||
object->SetProperties(*dictionary);
|
||||
} else {
|
||||
dictionary->SetEntry(entry, *name, *value, details);
|
||||
}
|
||||
} else {
|
||||
PropertyDetails original_details = dictionary->DetailsAt(entry);
|
||||
int enumeration_index = original_details.dictionary_index();
|
||||
DCHECK_GT(enumeration_index, 0);
|
||||
details = details.set_index(enumeration_index);
|
||||
dictionary->SetEntry(entry, *name, *value, details);
|
||||
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
|
||||
InternalIndex entry = dictionary->FindEntry(isolate, name);
|
||||
if (entry.is_not_found()) {
|
||||
DCHECK_IMPLIES(object->map().is_prototype_map(),
|
||||
Map::IsPrototypeChainInvalidated(object->map()));
|
||||
dictionary =
|
||||
NameDictionary::Add(isolate, dictionary, name, value, details);
|
||||
object->SetProperties(*dictionary);
|
||||
} else {
|
||||
PropertyDetails original_details = dictionary->DetailsAt(entry);
|
||||
int enumeration_index = original_details.dictionary_index();
|
||||
DCHECK_GT(enumeration_index, 0);
|
||||
details = details.set_index(enumeration_index);
|
||||
dictionary->SetEntry(entry, *name, *value, details);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2901,10 +2945,20 @@ void MigrateFastToSlow(Isolate* isolate, Handle<JSObject> object,
|
||||
property_count += expected_additional_properties;
|
||||
} else {
|
||||
// Make space for two more properties.
|
||||
property_count += NameDictionary::kInitialCapacity;
|
||||
int initial_capacity = V8_DICT_MODE_PROTOTYPES_BOOL
|
||||
? OrderedNameDictionary::kInitialCapacity
|
||||
: NameDictionary::kInitialCapacity;
|
||||
property_count += initial_capacity;
|
||||
}
|
||||
|
||||
Handle<NameDictionary> dictionary;
|
||||
Handle<OrderedNameDictionary> ord_dictionary;
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
ord_dictionary =
|
||||
isolate->factory()->NewOrderedNameDictionary(property_count);
|
||||
} else {
|
||||
dictionary = isolate->factory()->NewNameDictionary(property_count);
|
||||
}
|
||||
Handle<NameDictionary> dictionary =
|
||||
NameDictionary::New(isolate, property_count);
|
||||
|
||||
Handle<DescriptorArray> descs(
|
||||
map->instance_descriptors(isolate, kRelaxedLoad), isolate);
|
||||
@ -2938,11 +2992,19 @@ void MigrateFastToSlow(Isolate* isolate, Handle<JSObject> object,
|
||||
DCHECK(!value.is_null());
|
||||
PropertyDetails d(details.kind(), details.attributes(),
|
||||
PropertyCellType::kNoCell);
|
||||
dictionary = NameDictionary::Add(isolate, dictionary, key, value, d);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
ord_dictionary =
|
||||
OrderedNameDictionary::Add(isolate, ord_dictionary, key, value, d)
|
||||
.ToHandleChecked();
|
||||
} else {
|
||||
dictionary = NameDictionary::Add(isolate, dictionary, key, value, d);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the next enumeration index from instance descriptor.
|
||||
dictionary->set_next_enumeration_index(real_size + 1);
|
||||
if (!V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
// Copy the next enumeration index from instance descriptor.
|
||||
dictionary->set_next_enumeration_index(real_size + 1);
|
||||
}
|
||||
|
||||
// From here on we cannot fail and we shouldn't GC anymore.
|
||||
DisallowHeapAllocation no_allocation;
|
||||
@ -2970,7 +3032,11 @@ void MigrateFastToSlow(Isolate* isolate, Handle<JSObject> object,
|
||||
// the left-over space to avoid races with the sweeper thread.
|
||||
object->synchronized_set_map(*new_map);
|
||||
|
||||
object->SetProperties(*dictionary);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
object->SetProperties(*ord_dictionary);
|
||||
} else {
|
||||
object->SetProperties(*dictionary);
|
||||
}
|
||||
|
||||
// Ensure that in-object space of slow-mode object does not contain random
|
||||
// garbage.
|
||||
@ -3352,26 +3418,52 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
||||
DCHECK(!object->IsJSGlobalObject());
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
|
||||
|
||||
Handle<NameDictionary> dictionary;
|
||||
Handle<OrderedNameDictionary> ord_dictionary;
|
||||
int number_of_elements;
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
ord_dictionary = handle(object->property_dictionary_ordered(), isolate);
|
||||
number_of_elements = ord_dictionary->NumberOfElements();
|
||||
} else {
|
||||
dictionary = handle(object->property_dictionary(), isolate);
|
||||
number_of_elements = dictionary->NumberOfElements();
|
||||
}
|
||||
|
||||
// Make sure we preserve dictionary representation if there are too many
|
||||
// descriptors.
|
||||
int number_of_elements = dictionary->NumberOfElements();
|
||||
if (number_of_elements > kMaxNumberOfDescriptors) return;
|
||||
|
||||
Handle<FixedArray> iteration_order =
|
||||
NameDictionary::IterationIndices(isolate, dictionary);
|
||||
Handle<FixedArray> iteration_order;
|
||||
int iteration_length;
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
// |iteration_order| remains empty handle, we don't need it.
|
||||
iteration_length = ord_dictionary->UsedCapacity();
|
||||
} else {
|
||||
iteration_order = NameDictionary::IterationIndices(isolate, dictionary);
|
||||
iteration_length = dictionary->NumberOfElements();
|
||||
}
|
||||
|
||||
int instance_descriptor_length = iteration_order->length();
|
||||
int number_of_fields = 0;
|
||||
|
||||
// Compute the length of the instance descriptor.
|
||||
ReadOnlyRoots roots(isolate);
|
||||
for (int i = 0; i < instance_descriptor_length; i++) {
|
||||
InternalIndex index(Smi::ToInt(iteration_order->get(i)));
|
||||
DCHECK(dictionary->IsKey(roots, dictionary->KeyAt(isolate, index)));
|
||||
for (int i = 0; i < iteration_length; i++) {
|
||||
PropertyKind kind;
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
InternalIndex index(i);
|
||||
Object key = ord_dictionary->KeyAt(index);
|
||||
if (!OrderedNameDictionary::IsKey(roots, key)) {
|
||||
// Ignore deleted entries.
|
||||
continue;
|
||||
}
|
||||
kind = ord_dictionary->DetailsAt(index).kind();
|
||||
} else {
|
||||
InternalIndex index(Smi::ToInt(iteration_order->get(i)));
|
||||
DCHECK(dictionary->IsKey(roots, dictionary->KeyAt(isolate, index)));
|
||||
kind = dictionary->DetailsAt(index).kind();
|
||||
}
|
||||
|
||||
PropertyKind kind = dictionary->DetailsAt(index).kind();
|
||||
if (kind == kData) {
|
||||
number_of_fields += 1;
|
||||
}
|
||||
@ -3391,7 +3483,7 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
||||
|
||||
NotifyMapChange(old_map, new_map, isolate);
|
||||
|
||||
if (instance_descriptor_length == 0) {
|
||||
if (number_of_elements == 0) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
DCHECK_LE(unused_property_fields, inobject_props);
|
||||
// Transform the object.
|
||||
@ -3408,7 +3500,7 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
||||
|
||||
// Allocate the instance descriptor.
|
||||
Handle<DescriptorArray> descriptors =
|
||||
DescriptorArray::Allocate(isolate, instance_descriptor_length, 0);
|
||||
DescriptorArray::Allocate(isolate, number_of_elements, 0);
|
||||
|
||||
int number_of_allocated_fields =
|
||||
number_of_fields + unused_property_fields - inobject_props;
|
||||
@ -3427,9 +3519,30 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
||||
|
||||
// Fill in the instance descriptor and the fields.
|
||||
int current_offset = 0;
|
||||
for (int i = 0; i < instance_descriptor_length; i++) {
|
||||
InternalIndex index(Smi::ToInt(iteration_order->get(i)));
|
||||
Name k = dictionary->NameAt(index);
|
||||
int descriptor_index = 0;
|
||||
for (int i = 0; i < iteration_length; i++) {
|
||||
Name k;
|
||||
Object value;
|
||||
PropertyDetails details = PropertyDetails::Empty();
|
||||
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
InternalIndex index(i);
|
||||
Object key_obj = ord_dictionary->KeyAt(index);
|
||||
if (!OrderedNameDictionary::IsKey(roots, key_obj)) {
|
||||
continue;
|
||||
}
|
||||
k = Name::cast(key_obj);
|
||||
|
||||
value = ord_dictionary->ValueAt(index);
|
||||
details = ord_dictionary->DetailsAt(index);
|
||||
} else {
|
||||
InternalIndex index(Smi::ToInt(iteration_order->get(i)));
|
||||
k = dictionary->NameAt(index);
|
||||
|
||||
value = dictionary->ValueAt(index);
|
||||
details = dictionary->DetailsAt(index);
|
||||
}
|
||||
|
||||
// Dictionary keys are internalized upon insertion.
|
||||
// TODO(jkummerow): Turn this into a DCHECK if it's not hit in the wild.
|
||||
CHECK(k.IsUniqueName());
|
||||
@ -3440,9 +3553,6 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
||||
new_map->set_may_have_interesting_symbols(true);
|
||||
}
|
||||
|
||||
Object value = dictionary->ValueAt(index);
|
||||
|
||||
PropertyDetails details = dictionary->DetailsAt(index);
|
||||
DCHECK_EQ(kField, details.location());
|
||||
DCHECK_EQ(PropertyConstness::kMutable, details.constness());
|
||||
|
||||
@ -3473,9 +3583,10 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object,
|
||||
}
|
||||
current_offset += details.field_width_in_words();
|
||||
}
|
||||
descriptors->Set(InternalIndex(i), &d);
|
||||
descriptors->Set(InternalIndex(descriptor_index++), &d);
|
||||
}
|
||||
DCHECK(current_offset == number_of_fields);
|
||||
DCHECK_EQ(current_offset, number_of_fields);
|
||||
DCHECK_EQ(descriptor_index, number_of_elements);
|
||||
|
||||
descriptors->Sort();
|
||||
|
||||
@ -3685,8 +3796,13 @@ bool TestPropertiesIntegrityLevel(JSObject object, PropertyAttributes level) {
|
||||
return TestFastPropertiesIntegrityLevel(object.map(), level);
|
||||
}
|
||||
|
||||
return TestDictionaryPropertiesIntegrityLevel(
|
||||
object.property_dictionary(), object.GetReadOnlyRoots(), level);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
return TestDictionaryPropertiesIntegrityLevel(
|
||||
object.property_dictionary_ordered(), object.GetReadOnlyRoots(), level);
|
||||
} else {
|
||||
return TestDictionaryPropertiesIntegrityLevel(
|
||||
object.property_dictionary(), object.GetReadOnlyRoots(), level);
|
||||
}
|
||||
}
|
||||
|
||||
bool TestElementsIntegrityLevel(JSObject object, PropertyAttributes level) {
|
||||
@ -3984,6 +4100,11 @@ Maybe<bool> JSObject::PreventExtensionsWithTransition(
|
||||
JSGlobalObject::cast(*object).global_dictionary(), isolate);
|
||||
JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
|
||||
attrs);
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
Handle<OrderedNameDictionary> dictionary(
|
||||
object->property_dictionary_ordered(), isolate);
|
||||
JSObject::ApplyAttributesToDictionary(isolate, roots, dictionary,
|
||||
attrs);
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(object->property_dictionary(),
|
||||
isolate);
|
||||
@ -4239,6 +4360,8 @@ Object JSObject::SlowReverseLookup(Object value) {
|
||||
} else if (IsJSGlobalObject()) {
|
||||
return JSGlobalObject::cast(*this).global_dictionary().SlowReverseLookup(
|
||||
value);
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
return property_dictionary_ordered().SlowReverseLookup(GetIsolate(), value);
|
||||
} else {
|
||||
return property_dictionary().SlowReverseLookup(value);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "src/objects/keys.h"
|
||||
|
||||
#include "src/api/api-arguments-inl.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/execution/isolate-inl.h"
|
||||
#include "src/handles/handles-inl.h"
|
||||
#include "src/heap/factory.h"
|
||||
@ -812,61 +813,93 @@ base::Optional<int> CollectOwnPropertyNamesInternal(
|
||||
return first_skipped;
|
||||
}
|
||||
|
||||
// Copies enumerable keys to preallocated fixed array.
|
||||
// Does not throw for uninitialized exports in module namespace objects, so
|
||||
// this has to be checked separately.
|
||||
template <typename Dict>
|
||||
void CopyEnumKeysTo(Isolate* isolate, Handle<Dict> dictionary,
|
||||
Handle<FixedArray> storage, KeyCollectionMode mode,
|
||||
KeyAccumulator* accumulator) {
|
||||
// Logic shared between different specializations of CopyEnumKeysTo.
|
||||
template <typename Dictionary>
|
||||
void CommonCopyEnumKeysTo(Isolate* isolate, Handle<Dictionary> dictionary,
|
||||
Handle<FixedArray> storage, KeyCollectionMode mode,
|
||||
KeyAccumulator* accumulator) {
|
||||
DCHECK_IMPLIES(mode != KeyCollectionMode::kOwnOnly, accumulator != nullptr);
|
||||
int length = storage->length();
|
||||
int properties = 0;
|
||||
ReadOnlyRoots roots(isolate);
|
||||
{
|
||||
AllowHeapAllocation allow_gc;
|
||||
for (InternalIndex i : dictionary->IterateEntries()) {
|
||||
Object key;
|
||||
if (!dictionary->ToKey(roots, i, &key)) continue;
|
||||
bool is_shadowing_key = false;
|
||||
if (key.IsSymbol()) continue;
|
||||
PropertyDetails details = dictionary->DetailsAt(i);
|
||||
if (details.IsDontEnum()) {
|
||||
if (mode == KeyCollectionMode::kIncludePrototypes) {
|
||||
is_shadowing_key = true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (is_shadowing_key) {
|
||||
// This might allocate, but {key} is not used afterwards.
|
||||
accumulator->AddShadowingKey(key, &allow_gc);
|
||||
continue;
|
||||
|
||||
AllowHeapAllocation allow_gc;
|
||||
for (InternalIndex i : dictionary->IterateEntries()) {
|
||||
Object key;
|
||||
if (!dictionary->ToKey(roots, i, &key)) continue;
|
||||
bool is_shadowing_key = false;
|
||||
if (key.IsSymbol()) continue;
|
||||
PropertyDetails details = dictionary->DetailsAt(i);
|
||||
if (details.IsDontEnum()) {
|
||||
if (mode == KeyCollectionMode::kIncludePrototypes) {
|
||||
is_shadowing_key = true;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (is_shadowing_key) {
|
||||
// This might allocate, but {key} is not used afterwards.
|
||||
accumulator->AddShadowingKey(key, &allow_gc);
|
||||
continue;
|
||||
} else {
|
||||
if (Dictionary::kIsOrderedDictionaryType) {
|
||||
storage->set(properties, dictionary->ValueAt(i));
|
||||
} else {
|
||||
// If the dictionary does not store elements in enumeration order,
|
||||
// we need to sort it afterwards in CopyEnumKeysTo. To enable this we
|
||||
// need to store indices at this point, rather than the values at the
|
||||
// given indices.
|
||||
storage->set(properties, Smi::FromInt(i.as_int()));
|
||||
}
|
||||
properties++;
|
||||
if (mode == KeyCollectionMode::kOwnOnly && properties == length) break;
|
||||
}
|
||||
properties++;
|
||||
if (mode == KeyCollectionMode::kOwnOnly && properties == length) break;
|
||||
}
|
||||
|
||||
CHECK_EQ(length, properties);
|
||||
{
|
||||
DisallowHeapAllocation no_gc;
|
||||
Dict raw_dictionary = *dictionary;
|
||||
FixedArray raw_storage = *storage;
|
||||
EnumIndexComparator<Dict> cmp(raw_dictionary);
|
||||
// Use AtomicSlot wrapper to ensure that std::sort uses atomic load and
|
||||
// store operations that are safe for concurrent marking.
|
||||
AtomicSlot start(storage->GetFirstElementAddress());
|
||||
std::sort(start, start + length, cmp);
|
||||
for (int i = 0; i < length; i++) {
|
||||
InternalIndex index(Smi::ToInt(raw_storage.get(i)));
|
||||
raw_storage.set(i, raw_dictionary.NameAt(index));
|
||||
}
|
||||
}
|
||||
|
||||
// Copies enumerable keys to preallocated fixed array.
|
||||
// Does not throw for uninitialized exports in module namespace objects, so
|
||||
// this has to be checked separately.
|
||||
template <typename Dictionary>
|
||||
void CopyEnumKeysTo(Isolate* isolate, Handle<Dictionary> dictionary,
|
||||
Handle<FixedArray> storage, KeyCollectionMode mode,
|
||||
KeyAccumulator* accumulator) {
|
||||
STATIC_ASSERT(!Dictionary::kIsOrderedDictionaryType);
|
||||
|
||||
CommonCopyEnumKeysTo<Dictionary>(isolate, dictionary, storage, mode,
|
||||
accumulator);
|
||||
|
||||
int length = storage->length();
|
||||
|
||||
DisallowHeapAllocation no_gc;
|
||||
Dictionary raw_dictionary = *dictionary;
|
||||
FixedArray raw_storage = *storage;
|
||||
EnumIndexComparator<Dictionary> cmp(raw_dictionary);
|
||||
// Use AtomicSlot wrapper to ensure that std::sort uses atomic load and
|
||||
// store operations that are safe for concurrent marking.
|
||||
AtomicSlot start(storage->GetFirstElementAddress());
|
||||
std::sort(start, start + length, cmp);
|
||||
for (int i = 0; i < length; i++) {
|
||||
InternalIndex index(Smi::ToInt(raw_storage.get(i)));
|
||||
raw_storage.set(i, raw_dictionary.NameAt(index));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void CopyEnumKeysTo(Isolate* isolate, Handle<OrderedNameDictionary> dictionary,
|
||||
Handle<FixedArray> storage, KeyCollectionMode mode,
|
||||
KeyAccumulator* accumulator) {
|
||||
CommonCopyEnumKeysTo<OrderedNameDictionary>(isolate, dictionary, storage,
|
||||
mode, accumulator);
|
||||
|
||||
// No need to sort, as CommonCopyEnumKeysTo on OrderedNameDictionary
|
||||
// adds entries to |storage| in the dict's insertion order
|
||||
// Further, the template argument true above means that |storage|
|
||||
// now contains the actual values from |dictionary|, rather than indices.
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
|
||||
KeyCollectionMode mode,
|
||||
@ -917,14 +950,20 @@ ExceptionStatus CollectKeysFromDictionary(Handle<Dictionary> dictionary,
|
||||
if (!accessors.IsAccessorInfo()) continue;
|
||||
if (!AccessorInfo::cast(accessors).all_can_read()) continue;
|
||||
}
|
||||
// TODO(emrich): consider storing keys instead of indices into the array
|
||||
// in case of ordered dictionary type.
|
||||
array->set(array_size++, Smi::FromInt(i.as_int()));
|
||||
}
|
||||
if (!Dictionary::kIsOrderedDictionaryType) {
|
||||
// Sorting only needed if it's an unordered dictionary,
|
||||
// otherwise we traversed elements in insertion order
|
||||
|
||||
EnumIndexComparator<Dictionary> cmp(*dictionary);
|
||||
// Use AtomicSlot wrapper to ensure that std::sort uses atomic load and
|
||||
// store operations that are safe for concurrent marking.
|
||||
AtomicSlot start(array->GetFirstElementAddress());
|
||||
std::sort(start, start + array_size, cmp);
|
||||
EnumIndexComparator<Dictionary> cmp(*dictionary);
|
||||
// Use AtomicSlot wrapper to ensure that std::sort uses atomic load and
|
||||
// store operations that are safe for concurrent marking.
|
||||
AtomicSlot start(array->GetFirstElementAddress());
|
||||
std::sort(start, start + array_size, cmp);
|
||||
}
|
||||
}
|
||||
|
||||
bool has_seen_symbol = false;
|
||||
@ -978,6 +1017,9 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
|
||||
enum_keys = GetOwnEnumPropertyDictionaryKeys(
|
||||
isolate_, mode_, this, object,
|
||||
JSGlobalObject::cast(*object).global_dictionary());
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
enum_keys = GetOwnEnumPropertyDictionaryKeys(
|
||||
isolate_, mode_, this, object, object->property_dictionary_ordered());
|
||||
} else {
|
||||
enum_keys = GetOwnEnumPropertyDictionaryKeys(
|
||||
isolate_, mode_, this, object, object->property_dictionary());
|
||||
@ -1013,6 +1055,9 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
|
||||
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_),
|
||||
this));
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(object->property_dictionary_ordered(), isolate_), this));
|
||||
} else {
|
||||
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(object->property_dictionary(), isolate_), this));
|
||||
@ -1034,6 +1079,9 @@ ExceptionStatus KeyAccumulator::CollectPrivateNames(Handle<JSReceiver> receiver,
|
||||
RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_),
|
||||
this));
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(object->property_dictionary_ordered(), isolate_), this));
|
||||
} else {
|
||||
RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(object->property_dictionary(), isolate_), this));
|
||||
@ -1116,6 +1164,10 @@ Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
|
||||
return GetOwnEnumPropertyDictionaryKeys(
|
||||
isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
|
||||
JSGlobalObject::cast(*object).global_dictionary());
|
||||
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
return GetOwnEnumPropertyDictionaryKeys(
|
||||
isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
|
||||
object->property_dictionary_ordered());
|
||||
} else {
|
||||
return GetOwnEnumPropertyDictionaryKeys(
|
||||
isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
|
||||
@ -1146,8 +1198,13 @@ Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
|
||||
Handle<JSProxy> proxy) {
|
||||
STACK_CHECK(isolate_, Nothing<bool>());
|
||||
if (filter_ == PRIVATE_NAMES_ONLY) {
|
||||
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(proxy->property_dictionary(), isolate_), this));
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(proxy->property_dictionary_ordered(), isolate_), this));
|
||||
} else {
|
||||
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
|
||||
handle(proxy->property_dictionary(), isolate_), this));
|
||||
}
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "src/objects/lookup.h"
|
||||
|
||||
#include "src/common/globals.h"
|
||||
#include "src/deoptimizer/deoptimizer.h"
|
||||
#include "src/execution/isolate-inl.h"
|
||||
#include "src/execution/protectors-inl.h"
|
||||
@ -14,6 +15,7 @@
|
||||
#include "src/objects/field-type.h"
|
||||
#include "src/objects/hash-table-inl.h"
|
||||
#include "src/objects/heap-number-inl.h"
|
||||
#include "src/objects/ordered-hash-table.h"
|
||||
#include "src/objects/struct-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -510,15 +512,24 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
|
||||
cell->set_value(*value);
|
||||
property_details_ = cell->property_details();
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(
|
||||
holder_obj->property_dictionary(isolate_), isolate());
|
||||
PropertyDetails original_details =
|
||||
dictionary->DetailsAt(dictionary_entry());
|
||||
int enumeration_index = original_details.dictionary_index();
|
||||
DCHECK_GT(enumeration_index, 0);
|
||||
details = details.set_index(enumeration_index);
|
||||
dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
|
||||
property_details_ = details;
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
Handle<OrderedNameDictionary> dictionary(
|
||||
holder_obj->property_dictionary_ordered(isolate_), isolate());
|
||||
dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
|
||||
DCHECK_EQ(details.AsSmi(),
|
||||
dictionary->DetailsAt(dictionary_entry()).AsSmi());
|
||||
property_details_ = details;
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(
|
||||
holder_obj->property_dictionary(isolate_), isolate());
|
||||
PropertyDetails original_details =
|
||||
dictionary->DetailsAt(dictionary_entry());
|
||||
int enumeration_index = original_details.dictionary_index();
|
||||
DCHECK_GT(enumeration_index, 0);
|
||||
details = details.set_index(enumeration_index);
|
||||
dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
|
||||
property_details_ = details;
|
||||
}
|
||||
}
|
||||
state_ = DATA;
|
||||
}
|
||||
@ -641,18 +652,35 @@ void LookupIterator::ApplyTransitionToDataProperty(
|
||||
property_details_ = transition->GetLastDescriptorDetails(isolate_);
|
||||
state_ = DATA;
|
||||
} else if (receiver->map(isolate_).is_dictionary_map()) {
|
||||
Handle<NameDictionary> dictionary(receiver->property_dictionary(isolate_),
|
||||
isolate_);
|
||||
if (receiver->map(isolate_).is_prototype_map() &&
|
||||
receiver->IsJSObject(isolate_)) {
|
||||
JSObject::InvalidatePrototypeChains(receiver->map(isolate_));
|
||||
}
|
||||
dictionary = NameDictionary::Add(isolate(), dictionary, name(),
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
Handle<OrderedNameDictionary> dictionary(
|
||||
receiver->property_dictionary_ordered(isolate_), isolate_);
|
||||
|
||||
dictionary =
|
||||
OrderedNameDictionary::Add(isolate(), dictionary, name(),
|
||||
isolate_->factory()->uninitialized_value(),
|
||||
property_details_, &number_);
|
||||
receiver->SetProperties(*dictionary);
|
||||
// Reload details containing proper enumeration index value.
|
||||
property_details_ = dictionary->DetailsAt(number_);
|
||||
property_details_)
|
||||
.ToHandleChecked();
|
||||
|
||||
// set to last used entry
|
||||
number_ = InternalIndex(dictionary->UsedCapacity() - 1);
|
||||
receiver->SetProperties(*dictionary);
|
||||
} else {
|
||||
Handle<NameDictionary> dictionary(receiver->property_dictionary(isolate_),
|
||||
isolate_);
|
||||
|
||||
dictionary =
|
||||
NameDictionary::Add(isolate(), dictionary, name(),
|
||||
isolate_->factory()->uninitialized_value(),
|
||||
property_details_, &number_);
|
||||
receiver->SetProperties(*dictionary);
|
||||
// Reload details containing proper enumeration index value.
|
||||
property_details_ = dictionary->DetailsAt(number_);
|
||||
}
|
||||
has_property_ = true;
|
||||
state_ = DATA;
|
||||
|
||||
@ -837,8 +865,13 @@ Handle<Object> LookupIterator::FetchValue(
|
||||
result = holder->global_dictionary(isolate_).ValueAt(isolate_,
|
||||
dictionary_entry());
|
||||
} else if (!holder_->HasFastProperties(isolate_)) {
|
||||
result = holder_->property_dictionary(isolate_).ValueAt(isolate_,
|
||||
dictionary_entry());
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
result = holder_->property_dictionary_ordered(isolate_).ValueAt(
|
||||
dictionary_entry());
|
||||
} else {
|
||||
result = holder_->property_dictionary(isolate_).ValueAt(
|
||||
isolate_, dictionary_entry());
|
||||
}
|
||||
} else if (property_details_.location() == kField) {
|
||||
DCHECK_EQ(kData, property_details_.kind());
|
||||
Handle<JSObject> holder = GetHolder<JSObject>();
|
||||
@ -994,8 +1027,14 @@ void LookupIterator::WriteDataValue(Handle<Object> value,
|
||||
dictionary.CellAt(isolate_, dictionary_entry()).set_value(*value);
|
||||
} else {
|
||||
DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
|
||||
NameDictionary dictionary = holder->property_dictionary(isolate_);
|
||||
dictionary.ValueAtPut(dictionary_entry(), *value);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
OrderedNameDictionary dictionary =
|
||||
holder->property_dictionary_ordered(isolate_);
|
||||
dictionary.ValueAtPut(dictionary_entry(), *value);
|
||||
} else {
|
||||
NameDictionary dictionary = holder->property_dictionary(isolate_);
|
||||
dictionary.ValueAtPut(dictionary_entry(), *value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1138,10 +1177,17 @@ LookupIterator::State LookupIterator::LookupInRegularHolder(
|
||||
property_details_ = descriptors.GetDetails(number_);
|
||||
} else {
|
||||
DCHECK_IMPLIES(holder.IsJSProxy(isolate_), name()->IsPrivate(isolate_));
|
||||
NameDictionary dict = holder.property_dictionary(isolate_);
|
||||
number_ = dict.FindEntry(isolate(), name_);
|
||||
if (number_.is_not_found()) return NotFound(holder);
|
||||
property_details_ = dict.DetailsAt(number_);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
OrderedNameDictionary dict = holder.property_dictionary_ordered(isolate_);
|
||||
number_ = dict.FindEntry(isolate(), *name_);
|
||||
if (number_.is_not_found()) return NotFound(holder);
|
||||
property_details_ = dict.DetailsAt(number_);
|
||||
} else {
|
||||
NameDictionary dict = holder.property_dictionary(isolate_);
|
||||
number_ = dict.FindEntry(isolate(), name_);
|
||||
if (number_.is_not_found()) return NotFound(holder);
|
||||
property_details_ = dict.DetailsAt(number_);
|
||||
}
|
||||
}
|
||||
has_property_ = true;
|
||||
switch (property_details_.kind()) {
|
||||
|
@ -3519,11 +3519,20 @@ Maybe<bool> JSProxy::SetPrivateSymbol(Isolate* isolate, Handle<JSProxy> proxy,
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
Handle<NameDictionary> dict(proxy->property_dictionary(), isolate);
|
||||
PropertyDetails details(kData, DONT_ENUM, PropertyCellType::kNoCell);
|
||||
Handle<NameDictionary> result =
|
||||
NameDictionary::Add(isolate, dict, private_name, value, details);
|
||||
if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
|
||||
if (V8_DICT_MODE_PROTOTYPES_BOOL) {
|
||||
Handle<OrderedNameDictionary> dict(proxy->property_dictionary_ordered(),
|
||||
isolate);
|
||||
Handle<OrderedNameDictionary> result =
|
||||
OrderedNameDictionary::Add(isolate, dict, private_name, value, details)
|
||||
.ToHandleChecked();
|
||||
if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
|
||||
} else {
|
||||
Handle<NameDictionary> dict(proxy->property_dictionary(), isolate);
|
||||
Handle<NameDictionary> result =
|
||||
NameDictionary::Add(isolate, dict, private_name, value, details);
|
||||
if (!dict.is_identical_to(result)) proxy->SetProperties(*result);
|
||||
}
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,12 @@ template <class Derived, int entrysize>
|
||||
OrderedHashTable<Derived, entrysize>::OrderedHashTable(Address ptr)
|
||||
: FixedArray(ptr) {}
|
||||
|
||||
template <class Derived, int entrysize>
|
||||
bool OrderedHashTable<Derived, entrysize>::IsKey(ReadOnlyRoots roots,
|
||||
Object k) {
|
||||
return k != roots.the_hole_value();
|
||||
}
|
||||
|
||||
OrderedHashSet::OrderedHashSet(Address ptr)
|
||||
: OrderedHashTable<OrderedHashSet, 1>(ptr) {
|
||||
SLOW_DCHECK(IsOrderedHashSet());
|
||||
@ -107,6 +113,10 @@ inline Object OrderedNameDictionary::ValueAt(InternalIndex entry) {
|
||||
return get(EntryToIndex(entry) + kValueOffset);
|
||||
}
|
||||
|
||||
Name OrderedNameDictionary::NameAt(InternalIndex entry) {
|
||||
return Name::cast(KeyAt(entry));
|
||||
}
|
||||
|
||||
// Set the value for entry.
|
||||
inline void OrderedNameDictionary::ValueAtPut(InternalIndex entry,
|
||||
Object value) {
|
||||
|
@ -23,8 +23,7 @@ MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Allocate(
|
||||
// from number of buckets. If we decide to change kLoadFactor
|
||||
// to something other than 2, capacity should be stored as another
|
||||
// field of this object.
|
||||
capacity =
|
||||
base::bits::RoundUpToPowerOfTwo32(Max(kMinNonZeroCapacity, capacity));
|
||||
capacity = base::bits::RoundUpToPowerOfTwo32(Max(kInitialCapacity, capacity));
|
||||
if (capacity > MaxCapacity()) {
|
||||
return MaybeHandle<Derived>();
|
||||
}
|
||||
@ -74,7 +73,7 @@ MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::EnsureGrowable(
|
||||
int new_capacity;
|
||||
if (capacity == 0) {
|
||||
// step from empty to minimum proper size
|
||||
new_capacity = kMinNonZeroCapacity;
|
||||
new_capacity = kInitialCapacity;
|
||||
} else if (nod >= (capacity >> 1)) {
|
||||
// Don't need to grow if we can simply clear out deleted entries instead.
|
||||
// Note that we can't compact in place, though, so we always allocate
|
||||
@ -108,7 +107,7 @@ Handle<Derived> OrderedHashTable<Derived, entrysize>::Clear(
|
||||
: AllocationType::kOld;
|
||||
|
||||
Handle<Derived> new_table =
|
||||
Allocate(isolate, kMinNonZeroCapacity, allocation_type).ToHandleChecked();
|
||||
Allocate(isolate, kInitialCapacity, allocation_type).ToHandleChecked();
|
||||
|
||||
if (table->NumberOfBuckets() > 0) {
|
||||
// Don't try to modify the empty canonical table which lives in RO space.
|
||||
@ -349,6 +348,17 @@ bool OrderedHashTable<Derived, entrysize>::Delete(Isolate* isolate,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parameter |roots| only here for compatibility with HashTable<...>::ToKey.
|
||||
template <class Derived, int entrysize>
|
||||
bool OrderedHashTable<Derived, entrysize>::ToKey(ReadOnlyRoots roots,
|
||||
InternalIndex entry,
|
||||
Object* out_key) {
|
||||
Object k = KeyAt(entry);
|
||||
if (!IsKey(roots, k)) return false;
|
||||
*out_key = k;
|
||||
return true;
|
||||
}
|
||||
|
||||
Address OrderedHashMap::GetHash(Isolate* isolate, Address raw_key) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Object key(raw_key);
|
||||
@ -430,6 +440,38 @@ InternalIndex OrderedNameDictionary::FindEntry(Isolate* isolate, Object key) {
|
||||
return InternalIndex::NotFound();
|
||||
}
|
||||
|
||||
// TODO(emrich): This is almost an identical copy of
|
||||
// Dictionary<..>::SlowReverseLookup.
|
||||
// Consolidate both versions elsewhere (e.g., hash-table-utils)?
|
||||
Object OrderedNameDictionary::SlowReverseLookup(Isolate* isolate,
|
||||
Object value) {
|
||||
ReadOnlyRoots roots(isolate);
|
||||
for (InternalIndex i : IterateEntries()) {
|
||||
Object k;
|
||||
if (!ToKey(roots, i, &k)) continue;
|
||||
Object e = this->ValueAt(i);
|
||||
if (e == value) return k;
|
||||
}
|
||||
return roots.undefined_value();
|
||||
}
|
||||
|
||||
// TODO(emrich): This is almost an identical copy of
|
||||
// HashTable<..>::NumberOfEnumerableProperties.
|
||||
// Consolidate both versions elsewhere (e.g., hash-table-utils)?
|
||||
int OrderedNameDictionary::NumberOfEnumerableProperties() {
|
||||
ReadOnlyRoots roots = this->GetReadOnlyRoots();
|
||||
int result = 0;
|
||||
for (InternalIndex i : this->IterateEntries()) {
|
||||
Object k;
|
||||
if (!this->ToKey(roots, i, &k)) continue;
|
||||
if (k.FilterKey(ENUMERABLE_STRINGS)) continue;
|
||||
PropertyDetails details = this->DetailsAt(i);
|
||||
PropertyAttributes attr = details.attributes();
|
||||
if ((attr & ONLY_ENUMERABLE) == 0) result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeHandle<OrderedNameDictionary> OrderedNameDictionary::Add(
|
||||
Isolate* isolate, Handle<OrderedNameDictionary> table, Handle<Name> key,
|
||||
Handle<Object> value, PropertyDetails details) {
|
||||
|
@ -81,12 +81,18 @@ class OrderedHashTable : public FixedArray {
|
||||
// Returns true if the OrderedHashTable contains the key
|
||||
static bool HasKey(Isolate* isolate, Derived table, Object key);
|
||||
|
||||
// Returns whether a potential key |k| returned by KeyAt is a real
|
||||
// key (meaning that it is not a hole).
|
||||
static inline bool IsKey(ReadOnlyRoots roots, Object k);
|
||||
|
||||
// Returns a true value if the OrderedHashTable contains the key and
|
||||
// the key has been deleted. This does not shrink the table.
|
||||
static bool Delete(Isolate* isolate, Derived table, Object key);
|
||||
|
||||
InternalIndex FindEntry(Isolate* isolate, Object key);
|
||||
|
||||
Object SlowReverseLookup(Isolate* isolate, Object value);
|
||||
|
||||
int NumberOfElements() const {
|
||||
return Smi::ToInt(get(NumberOfElementsIndex()));
|
||||
}
|
||||
@ -109,12 +115,16 @@ class OrderedHashTable : public FixedArray {
|
||||
return InternalIndex::Range(UsedCapacity());
|
||||
}
|
||||
|
||||
// use KeyAt(i)->IsTheHole(isolate) to determine if this is a deleted entry.
|
||||
// use IsKey to check if this is a deleted entry.
|
||||
Object KeyAt(InternalIndex entry) {
|
||||
DCHECK_LT(entry.as_int(), this->UsedCapacity());
|
||||
return get(EntryToIndex(entry));
|
||||
}
|
||||
|
||||
// Similar to KeyAt, but indicates whether the given entry is valid
|
||||
// (not deleted one)
|
||||
bool ToKey(ReadOnlyRoots roots, InternalIndex entry, Object* out_key);
|
||||
|
||||
bool IsObsolete() { return !get(NextTableIndex()).IsSmi(); }
|
||||
|
||||
// The next newer table. This is only valid if the table is obsolete.
|
||||
@ -133,7 +143,7 @@ class OrderedHashTable : public FixedArray {
|
||||
static const int kNotFound = -1;
|
||||
// The minimum capacity. Note that despite this value, 0 is also a permitted
|
||||
// capacity, indicating a table without any storage for elements.
|
||||
static const int kMinNonZeroCapacity = 4;
|
||||
static const int kInitialCapacity = 4;
|
||||
|
||||
static constexpr int PrefixIndex() { return 0; }
|
||||
|
||||
@ -752,6 +762,10 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary
|
||||
|
||||
InternalIndex FindEntry(Isolate* isolate, Object key);
|
||||
|
||||
int NumberOfEnumerableProperties();
|
||||
|
||||
Object SlowReverseLookup(Isolate* isolate, Object value);
|
||||
|
||||
static Handle<OrderedNameDictionary> DeleteEntry(
|
||||
Isolate* isolate, Handle<OrderedNameDictionary> table,
|
||||
InternalIndex entry);
|
||||
@ -769,6 +783,9 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary
|
||||
// Returns the value for entry.
|
||||
inline Object ValueAt(InternalIndex entry);
|
||||
|
||||
// Like KeyAt, but casts to Name
|
||||
inline Name NameAt(InternalIndex entry);
|
||||
|
||||
// Set the value for entry.
|
||||
inline void ValueAtPut(InternalIndex entry, Object value);
|
||||
|
||||
@ -789,6 +806,8 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary
|
||||
static const int kPropertyDetailsOffset = 2;
|
||||
static const int kPrefixSize = 1;
|
||||
|
||||
static const bool kIsOrderedDictionaryType = true;
|
||||
|
||||
OBJECT_CONSTRUCTORS(OrderedNameDictionary,
|
||||
OrderedHashTable<OrderedNameDictionary, 3>);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user