Commit 4b6455aa authored by Frank Emrich's avatar Frank Emrich Committed by Commit Bot

[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/+/2489692Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Frank Emrich <emrich@google.com>
Cr-Commit-Position: refs/heads/master@{#70952}
parent 5326c4f8
...@@ -3371,7 +3371,7 @@ TNode<NameDictionary> CodeStubAssembler::CopyNameDictionary( ...@@ -3371,7 +3371,7 @@ TNode<NameDictionary> CodeStubAssembler::CopyNameDictionary(
template <typename CollectionType> template <typename CollectionType>
TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTable() { 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 kBucketCount = kCapacity / CollectionType::kLoadFactor;
static const int kDataTableLength = kCapacity * CollectionType::kEntrySize; static const int kDataTableLength = kCapacity * CollectionType::kEntrySize;
static const int kFixedArrayLength = static const int kFixedArrayLength =
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <iomanip> #include <iomanip>
#include <memory> #include <memory>
#include "src/common/globals.h"
#include "src/compiler/node.h" #include "src/compiler/node.h"
#include "src/diagnostics/disasm.h" #include "src/diagnostics/disasm.h"
#include "src/diagnostics/disassembler.h" #include "src/diagnostics/disassembler.h"
...@@ -274,6 +275,8 @@ bool JSObject::PrintProperties(std::ostream& os) { // NOLINT ...@@ -274,6 +275,8 @@ bool JSObject::PrintProperties(std::ostream& os) { // NOLINT
return map().NumberOfOwnDescriptors() > 0; return map().NumberOfOwnDescriptors() > 0;
} else if (IsJSGlobalObject()) { } else if (IsJSGlobalObject()) {
JSGlobalObject::cast(*this).global_dictionary().Print(os); JSGlobalObject::cast(*this).global_dictionary().Print(os);
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
property_dictionary_ordered().Print(os);
} else { } else {
property_dictionary().Print(os); property_dictionary().Print(os);
} }
......
...@@ -520,23 +520,27 @@ Handle<SmallOrderedNameDictionary> Factory::NewSmallOrderedNameDictionary( ...@@ -520,23 +520,27 @@ Handle<SmallOrderedNameDictionary> Factory::NewSmallOrderedNameDictionary(
} }
Handle<OrderedHashSet> Factory::NewOrderedHashSet() { Handle<OrderedHashSet> Factory::NewOrderedHashSet() {
return OrderedHashSet::Allocate(isolate(), return OrderedHashSet::Allocate(isolate(), OrderedHashSet::kInitialCapacity,
OrderedHashSet::kMinNonZeroCapacity) AllocationType::kYoung)
.ToHandleChecked(); .ToHandleChecked();
} }
Handle<OrderedHashMap> Factory::NewOrderedHashMap() { Handle<OrderedHashMap> Factory::NewOrderedHashMap() {
return OrderedHashMap::Allocate(isolate(), return OrderedHashMap::Allocate(isolate(), OrderedHashMap::kInitialCapacity,
OrderedHashMap::kMinNonZeroCapacity) AllocationType::kYoung)
.ToHandleChecked(); .ToHandleChecked();
} }
Handle<OrderedNameDictionary> Factory::NewOrderedNameDictionary() { Handle<OrderedNameDictionary> Factory::NewOrderedNameDictionary(int capacity) {
return OrderedNameDictionary::Allocate( return OrderedNameDictionary::Allocate(isolate(), capacity,
isolate(), OrderedNameDictionary::kMinNonZeroCapacity) AllocationType::kYoung)
.ToHandleChecked(); .ToHandleChecked();
} }
Handle<NameDictionary> Factory::NewNameDictionary(int at_least_space_for) {
return NameDictionary::New(isolate(), at_least_space_for);
}
Handle<PropertyDescriptorObject> Factory::NewPropertyDescriptorObject() { Handle<PropertyDescriptorObject> Factory::NewPropertyDescriptorObject() {
Handle<PropertyDescriptorObject> object = Handle<PropertyDescriptorObject> object =
Handle<PropertyDescriptorObject>::cast( Handle<PropertyDescriptorObject>::cast(
......
...@@ -164,10 +164,17 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> { ...@@ -164,10 +164,17 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<FrameArray> NewFrameArray(int number_of_frames); 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<OrderedHashSet> NewOrderedHashSet();
Handle<OrderedHashMap> NewOrderedHashMap(); Handle<OrderedHashMap> NewOrderedHashMap();
Handle<OrderedNameDictionary> NewOrderedNameDictionary();
Handle<SmallOrderedHashSet> NewSmallOrderedHashSet( Handle<SmallOrderedHashSet> NewSmallOrderedHashSet(
int capacity = kSmallOrderedHashSetMinCapacity, int capacity = kSmallOrderedHashSetMinCapacity,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
......
...@@ -131,6 +131,8 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary ...@@ -131,6 +131,8 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary
static const int kObjectHashIndex = kNextEnumerationIndexIndex + 1; static const int kObjectHashIndex = kNextEnumerationIndexIndex + 1;
static const int kEntryValueIndex = 1; static const int kEntryValueIndex = 1;
static const bool kIsOrderedDictionaryType = false;
inline void SetHash(int hash); inline void SetHash(int hash);
inline int Hash() const; inline int Hash() const;
......
...@@ -640,9 +640,15 @@ void JSReceiver::initialize_properties(Isolate* isolate) { ...@@ -640,9 +640,15 @@ void JSReceiver::initialize_properties(Isolate* isolate) {
ReadOnlyRoots roots(isolate); ReadOnlyRoots roots(isolate);
DCHECK(!ObjectInYoungGeneration(roots.empty_fixed_array())); DCHECK(!ObjectInYoungGeneration(roots.empty_fixed_array()));
DCHECK(!ObjectInYoungGeneration(roots.empty_property_dictionary())); DCHECK(!ObjectInYoungGeneration(roots.empty_property_dictionary()));
DCHECK(!ObjectInYoungGeneration(roots.empty_ordered_property_dictionary()));
if (map(isolate).is_dictionary_map()) { if (map(isolate).is_dictionary_map()) {
WRITE_FIELD(*this, kPropertiesOrHashOffset, if (V8_DICT_MODE_PROTOTYPES_BOOL) {
roots.empty_property_dictionary()); WRITE_FIELD(*this, kPropertiesOrHashOffset,
roots.empty_ordered_property_dictionary());
} else {
WRITE_FIELD(*this, kPropertiesOrHashOffset,
roots.empty_property_dictionary());
}
} else { } else {
WRITE_FIELD(*this, kPropertiesOrHashOffset, roots.empty_fixed_array()); WRITE_FIELD(*this, kPropertiesOrHashOffset, roots.empty_fixed_array());
} }
...@@ -651,7 +657,8 @@ void JSReceiver::initialize_properties(Isolate* isolate) { ...@@ -651,7 +657,8 @@ void JSReceiver::initialize_properties(Isolate* isolate) {
DEF_GETTER(JSReceiver, HasFastProperties, bool) { DEF_GETTER(JSReceiver, HasFastProperties, bool) {
DCHECK(raw_properties_or_hash(isolate).IsSmi() || DCHECK(raw_properties_or_hash(isolate).IsSmi() ||
((raw_properties_or_hash(isolate).IsGlobalDictionary(isolate) || ((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())); map(isolate).is_dictionary_map()));
return !map(isolate).is_dictionary_map(); return !map(isolate).is_dictionary_map();
} }
......
This diff is collapsed.
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "src/objects/keys.h" #include "src/objects/keys.h"
#include "src/api/api-arguments-inl.h" #include "src/api/api-arguments-inl.h"
#include "src/common/globals.h"
#include "src/execution/isolate-inl.h" #include "src/execution/isolate-inl.h"
#include "src/handles/handles-inl.h" #include "src/handles/handles-inl.h"
#include "src/heap/factory.h" #include "src/heap/factory.h"
...@@ -812,61 +813,93 @@ base::Optional<int> CollectOwnPropertyNamesInternal( ...@@ -812,61 +813,93 @@ base::Optional<int> CollectOwnPropertyNamesInternal(
return first_skipped; return first_skipped;
} }
// Copies enumerable keys to preallocated fixed array. // Logic shared between different specializations of CopyEnumKeysTo.
// Does not throw for uninitialized exports in module namespace objects, so template <typename Dictionary>
// this has to be checked separately. void CommonCopyEnumKeysTo(Isolate* isolate, Handle<Dictionary> dictionary,
template <typename Dict> Handle<FixedArray> storage, KeyCollectionMode mode,
void CopyEnumKeysTo(Isolate* isolate, Handle<Dict> dictionary, KeyAccumulator* accumulator) {
Handle<FixedArray> storage, KeyCollectionMode mode,
KeyAccumulator* accumulator) {
DCHECK_IMPLIES(mode != KeyCollectionMode::kOwnOnly, accumulator != nullptr); DCHECK_IMPLIES(mode != KeyCollectionMode::kOwnOnly, accumulator != nullptr);
int length = storage->length(); int length = storage->length();
int properties = 0; int properties = 0;
ReadOnlyRoots roots(isolate); ReadOnlyRoots roots(isolate);
{
AllowHeapAllocation allow_gc; AllowHeapAllocation allow_gc;
for (InternalIndex i : dictionary->IterateEntries()) { for (InternalIndex i : dictionary->IterateEntries()) {
Object key; Object key;
if (!dictionary->ToKey(roots, i, &key)) continue; if (!dictionary->ToKey(roots, i, &key)) continue;
bool is_shadowing_key = false; bool is_shadowing_key = false;
if (key.IsSymbol()) continue; if (key.IsSymbol()) continue;
PropertyDetails details = dictionary->DetailsAt(i); PropertyDetails details = dictionary->DetailsAt(i);
if (details.IsDontEnum()) { if (details.IsDontEnum()) {
if (mode == KeyCollectionMode::kIncludePrototypes) { if (mode == KeyCollectionMode::kIncludePrototypes) {
is_shadowing_key = true; is_shadowing_key = true;
} else { } else {
continue;
}
}
if (is_shadowing_key) {
// This might allocate, but {key} is not used afterwards.
accumulator->AddShadowingKey(key, &allow_gc);
continue; 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 { } 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())); 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); CHECK_EQ(length, properties);
{ }
DisallowHeapAllocation no_gc;
Dict raw_dictionary = *dictionary; // Copies enumerable keys to preallocated fixed array.
FixedArray raw_storage = *storage; // Does not throw for uninitialized exports in module namespace objects, so
EnumIndexComparator<Dict> cmp(raw_dictionary); // this has to be checked separately.
// Use AtomicSlot wrapper to ensure that std::sort uses atomic load and template <typename Dictionary>
// store operations that are safe for concurrent marking. void CopyEnumKeysTo(Isolate* isolate, Handle<Dictionary> dictionary,
AtomicSlot start(storage->GetFirstElementAddress()); Handle<FixedArray> storage, KeyCollectionMode mode,
std::sort(start, start + length, cmp); KeyAccumulator* accumulator) {
for (int i = 0; i < length; i++) { STATIC_ASSERT(!Dictionary::kIsOrderedDictionaryType);
InternalIndex index(Smi::ToInt(raw_storage.get(i)));
raw_storage.set(i, raw_dictionary.NameAt(index)); 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> template <class T>
Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate, Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
KeyCollectionMode mode, KeyCollectionMode mode,
...@@ -917,14 +950,20 @@ ExceptionStatus CollectKeysFromDictionary(Handle<Dictionary> dictionary, ...@@ -917,14 +950,20 @@ ExceptionStatus CollectKeysFromDictionary(Handle<Dictionary> dictionary,
if (!accessors.IsAccessorInfo()) continue; if (!accessors.IsAccessorInfo()) continue;
if (!AccessorInfo::cast(accessors).all_can_read()) 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())); array->set(array_size++, Smi::FromInt(i.as_int()));
} }
if (!Dictionary::kIsOrderedDictionaryType) {
EnumIndexComparator<Dictionary> cmp(*dictionary); // Sorting only needed if it's an unordered dictionary,
// Use AtomicSlot wrapper to ensure that std::sort uses atomic load and // otherwise we traversed elements in insertion order
// store operations that are safe for concurrent marking.
AtomicSlot start(array->GetFirstElementAddress()); EnumIndexComparator<Dictionary> cmp(*dictionary);
std::sort(start, start + array_size, cmp); // 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; bool has_seen_symbol = false;
...@@ -978,6 +1017,9 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, ...@@ -978,6 +1017,9 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
enum_keys = GetOwnEnumPropertyDictionaryKeys( enum_keys = GetOwnEnumPropertyDictionaryKeys(
isolate_, mode_, this, object, isolate_, mode_, this, object,
JSGlobalObject::cast(*object).global_dictionary()); JSGlobalObject::cast(*object).global_dictionary());
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
enum_keys = GetOwnEnumPropertyDictionaryKeys(
isolate_, mode_, this, object, object->property_dictionary_ordered());
} else { } else {
enum_keys = GetOwnEnumPropertyDictionaryKeys( enum_keys = GetOwnEnumPropertyDictionaryKeys(
isolate_, mode_, this, object, object->property_dictionary()); isolate_, mode_, this, object, object->property_dictionary());
...@@ -1013,6 +1055,9 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, ...@@ -1013,6 +1055,9 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary( RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_), handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_),
this)); this));
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(object->property_dictionary_ordered(), isolate_), this));
} else { } else {
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary( RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(object->property_dictionary(), isolate_), this)); handle(object->property_dictionary(), isolate_), this));
...@@ -1034,6 +1079,9 @@ ExceptionStatus KeyAccumulator::CollectPrivateNames(Handle<JSReceiver> receiver, ...@@ -1034,6 +1079,9 @@ ExceptionStatus KeyAccumulator::CollectPrivateNames(Handle<JSReceiver> receiver,
RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary( RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_), handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_),
this)); this));
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(object->property_dictionary_ordered(), isolate_), this));
} else { } else {
RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary( RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(object->property_dictionary(), isolate_), this)); handle(object->property_dictionary(), isolate_), this));
...@@ -1116,6 +1164,10 @@ Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys( ...@@ -1116,6 +1164,10 @@ Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
return GetOwnEnumPropertyDictionaryKeys( return GetOwnEnumPropertyDictionaryKeys(
isolate, KeyCollectionMode::kOwnOnly, nullptr, object, isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
JSGlobalObject::cast(*object).global_dictionary()); JSGlobalObject::cast(*object).global_dictionary());
} else if (V8_DICT_MODE_PROTOTYPES_BOOL) {
return GetOwnEnumPropertyDictionaryKeys(
isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
object->property_dictionary_ordered());
} else { } else {
return GetOwnEnumPropertyDictionaryKeys( return GetOwnEnumPropertyDictionaryKeys(
isolate, KeyCollectionMode::kOwnOnly, nullptr, object, isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
...@@ -1146,8 +1198,13 @@ Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver, ...@@ -1146,8 +1198,13 @@ Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
Handle<JSProxy> proxy) { Handle<JSProxy> proxy) {
STACK_CHECK(isolate_, Nothing<bool>()); STACK_CHECK(isolate_, Nothing<bool>());
if (filter_ == PRIVATE_NAMES_ONLY) { if (filter_ == PRIVATE_NAMES_ONLY) {
RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary( if (V8_DICT_MODE_PROTOTYPES_BOOL) {
handle(proxy->property_dictionary(), isolate_), this)); 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); return Just(true);
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/objects/lookup.h" #include "src/objects/lookup.h"
#include "src/common/globals.h"
#include "src/deoptimizer/deoptimizer.h" #include "src/deoptimizer/deoptimizer.h"
#include "src/execution/isolate-inl.h" #include "src/execution/isolate-inl.h"
#include "src/execution/protectors-inl.h" #include "src/execution/protectors-inl.h"
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
#include "src/objects/field-type.h" #include "src/objects/field-type.h"
#include "src/objects/hash-table-inl.h" #include "src/objects/hash-table-inl.h"
#include "src/objects/heap-number-inl.h" #include "src/objects/heap-number-inl.h"
#include "src/objects/ordered-hash-table.h"
#include "src/objects/struct-inl.h" #include "src/objects/struct-inl.h"
namespace v8 { namespace v8 {
...@@ -510,15 +512,24 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value, ...@@ -510,15 +512,24 @@ void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
cell->set_value(*value); cell->set_value(*value);
property_details_ = cell->property_details(); property_details_ = cell->property_details();
} else { } else {
Handle<NameDictionary> dictionary( if (V8_DICT_MODE_PROTOTYPES_BOOL) {
holder_obj->property_dictionary(isolate_), isolate()); Handle<OrderedNameDictionary> dictionary(
PropertyDetails original_details = holder_obj->property_dictionary_ordered(isolate_), isolate());
dictionary->DetailsAt(dictionary_entry()); dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
int enumeration_index = original_details.dictionary_index(); DCHECK_EQ(details.AsSmi(),
DCHECK_GT(enumeration_index, 0); dictionary->DetailsAt(dictionary_entry()).AsSmi());
details = details.set_index(enumeration_index); property_details_ = details;
dictionary->SetEntry(dictionary_entry(), *name(), *value, details); } else {
property_details_ = details; 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; state_ = DATA;
} }
...@@ -641,18 +652,35 @@ void LookupIterator::ApplyTransitionToDataProperty( ...@@ -641,18 +652,35 @@ void LookupIterator::ApplyTransitionToDataProperty(
property_details_ = transition->GetLastDescriptorDetails(isolate_); property_details_ = transition->GetLastDescriptorDetails(isolate_);
state_ = DATA; state_ = DATA;
} else if (receiver->map(isolate_).is_dictionary_map()) { } else if (receiver->map(isolate_).is_dictionary_map()) {
Handle<NameDictionary> dictionary(receiver->property_dictionary(isolate_),
isolate_);
if (receiver->map(isolate_).is_prototype_map() && if (receiver->map(isolate_).is_prototype_map() &&
receiver->IsJSObject(isolate_)) { receiver->IsJSObject(isolate_)) {
JSObject::InvalidatePrototypeChains(receiver->map(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(), isolate_->factory()->uninitialized_value(),
property_details_, &number_); property_details_)
receiver->SetProperties(*dictionary); .ToHandleChecked();
// Reload details containing proper enumeration index value.
property_details_ = dictionary->DetailsAt(number_); // 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; has_property_ = true;
state_ = DATA; state_ = DATA;
...@@ -837,8 +865,13 @@ Handle<Object> LookupIterator::FetchValue( ...@@ -837,8 +865,13 @@ Handle<Object> LookupIterator::FetchValue(
result = holder->global_dictionary(isolate_).ValueAt(isolate_, result = holder->global_dictionary(isolate_).ValueAt(isolate_,
dictionary_entry()); dictionary_entry());
} else if (!holder_->HasFastProperties(isolate_)) { } else if (!holder_->HasFastProperties(isolate_)) {
result = holder_->property_dictionary(isolate_).ValueAt(isolate_, if (V8_DICT_MODE_PROTOTYPES_BOOL) {
dictionary_entry()); 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) { } else if (property_details_.location() == kField) {
DCHECK_EQ(kData, property_details_.kind()); DCHECK_EQ(kData, property_details_.kind());
Handle<JSObject> holder = GetHolder<JSObject>(); Handle<JSObject> holder = GetHolder<JSObject>();
...@@ -994,8 +1027,14 @@ void LookupIterator::WriteDataValue(Handle<Object> value, ...@@ -994,8 +1027,14 @@ void LookupIterator::WriteDataValue(Handle<Object> value,
dictionary.CellAt(isolate_, dictionary_entry()).set_value(*value); dictionary.CellAt(isolate_, dictionary_entry()).set_value(*value);
} else { } else {
DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_)); DCHECK_IMPLIES(holder->IsJSProxy(isolate_), name()->IsPrivate(isolate_));
NameDictionary dictionary = holder->property_dictionary(isolate_); if (V8_DICT_MODE_PROTOTYPES_BOOL) {
dictionary.ValueAtPut(dictionary_entry(), *value); 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( ...@@ -1138,10 +1177,17 @@ LookupIterator::State LookupIterator::LookupInRegularHolder(
property_details_ = descriptors.GetDetails(number_); property_details_ = descriptors.GetDetails(number_);
} else { } else {
DCHECK_IMPLIES(holder.IsJSProxy(isolate_), name()->IsPrivate(isolate_)); DCHECK_IMPLIES(holder.IsJSProxy(isolate_), name()->IsPrivate(isolate_));
NameDictionary dict = holder.property_dictionary(isolate_); if (V8_DICT_MODE_PROTOTYPES_BOOL) {
number_ = dict.FindEntry(isolate(), name_); OrderedNameDictionary dict = holder.property_dictionary_ordered(isolate_);
if (number_.is_not_found()) return NotFound(holder); number_ = dict.FindEntry(isolate(), *name_);
property_details_ = dict.DetailsAt(number_); 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; has_property_ = true;
switch (property_details_.kind()) { switch (property_details_.kind()) {
......
...@@ -3519,11 +3519,20 @@ Maybe<bool> JSProxy::SetPrivateSymbol(Isolate* isolate, Handle<JSProxy> proxy, ...@@ -3519,11 +3519,20 @@ Maybe<bool> JSProxy::SetPrivateSymbol(Isolate* isolate, Handle<JSProxy> proxy,
return Just(true); return Just(true);
} }
Handle<NameDictionary> dict(proxy->property_dictionary(), isolate);
PropertyDetails details(kData, DONT_ENUM, PropertyCellType::kNoCell); PropertyDetails details(kData, DONT_ENUM, PropertyCellType::kNoCell);
Handle<NameDictionary> result = if (V8_DICT_MODE_PROTOTYPES_BOOL) {
NameDictionary::Add(isolate, dict, private_name, value, details); Handle<OrderedNameDictionary> dict(proxy->property_dictionary_ordered(),
if (!dict.is_identical_to(result)) proxy->SetProperties(*result); 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); return Just(true);
} }
......
...@@ -31,6 +31,12 @@ template <class Derived, int entrysize> ...@@ -31,6 +31,12 @@ template <class Derived, int entrysize>
OrderedHashTable<Derived, entrysize>::OrderedHashTable(Address ptr) OrderedHashTable<Derived, entrysize>::OrderedHashTable(Address ptr)
: FixedArray(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) OrderedHashSet::OrderedHashSet(Address ptr)
: OrderedHashTable<OrderedHashSet, 1>(ptr) { : OrderedHashTable<OrderedHashSet, 1>(ptr) {
SLOW_DCHECK(IsOrderedHashSet()); SLOW_DCHECK(IsOrderedHashSet());
...@@ -107,6 +113,10 @@ inline Object OrderedNameDictionary::ValueAt(InternalIndex entry) { ...@@ -107,6 +113,10 @@ inline Object OrderedNameDictionary::ValueAt(InternalIndex entry) {
return get(EntryToIndex(entry) + kValueOffset); return get(EntryToIndex(entry) + kValueOffset);
} }
Name OrderedNameDictionary::NameAt(InternalIndex entry) {
return Name::cast(KeyAt(entry));
}
// Set the value for entry. // Set the value for entry.
inline void OrderedNameDictionary::ValueAtPut(InternalIndex entry, inline void OrderedNameDictionary::ValueAtPut(InternalIndex entry,
Object value) { Object value) {
......
...@@ -23,8 +23,7 @@ MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Allocate( ...@@ -23,8 +23,7 @@ MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::Allocate(
// from number of buckets. If we decide to change kLoadFactor // from number of buckets. If we decide to change kLoadFactor
// to something other than 2, capacity should be stored as another // to something other than 2, capacity should be stored as another
// field of this object. // field of this object.
capacity = capacity = base::bits::RoundUpToPowerOfTwo32(Max(kInitialCapacity, capacity));
base::bits::RoundUpToPowerOfTwo32(Max(kMinNonZeroCapacity, capacity));
if (capacity > MaxCapacity()) { if (capacity > MaxCapacity()) {
return MaybeHandle<Derived>(); return MaybeHandle<Derived>();
} }
...@@ -74,7 +73,7 @@ MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::EnsureGrowable( ...@@ -74,7 +73,7 @@ MaybeHandle<Derived> OrderedHashTable<Derived, entrysize>::EnsureGrowable(
int new_capacity; int new_capacity;
if (capacity == 0) { if (capacity == 0) {
// step from empty to minimum proper size // step from empty to minimum proper size
new_capacity = kMinNonZeroCapacity; new_capacity = kInitialCapacity;
} else if (nod >= (capacity >> 1)) { } else if (nod >= (capacity >> 1)) {
// Don't need to grow if we can simply clear out deleted entries instead. // 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 // Note that we can't compact in place, though, so we always allocate
...@@ -108,7 +107,7 @@ Handle<Derived> OrderedHashTable<Derived, entrysize>::Clear( ...@@ -108,7 +107,7 @@ Handle<Derived> OrderedHashTable<Derived, entrysize>::Clear(
: AllocationType::kOld; : AllocationType::kOld;
Handle<Derived> new_table = Handle<Derived> new_table =
Allocate(isolate, kMinNonZeroCapacity, allocation_type).ToHandleChecked(); Allocate(isolate, kInitialCapacity, allocation_type).ToHandleChecked();
if (table->NumberOfBuckets() > 0) { if (table->NumberOfBuckets() > 0) {
// Don't try to modify the empty canonical table which lives in RO space. // 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, ...@@ -349,6 +348,17 @@ bool OrderedHashTable<Derived, entrysize>::Delete(Isolate* isolate,
return true; 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) { Address OrderedHashMap::GetHash(Isolate* isolate, Address raw_key) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
Object key(raw_key); Object key(raw_key);
...@@ -430,6 +440,38 @@ InternalIndex OrderedNameDictionary::FindEntry(Isolate* isolate, Object key) { ...@@ -430,6 +440,38 @@ InternalIndex OrderedNameDictionary::FindEntry(Isolate* isolate, Object key) {
return InternalIndex::NotFound(); 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( MaybeHandle<OrderedNameDictionary> OrderedNameDictionary::Add(
Isolate* isolate, Handle<OrderedNameDictionary> table, Handle<Name> key, Isolate* isolate, Handle<OrderedNameDictionary> table, Handle<Name> key,
Handle<Object> value, PropertyDetails details) { Handle<Object> value, PropertyDetails details) {
......
...@@ -81,12 +81,18 @@ class OrderedHashTable : public FixedArray { ...@@ -81,12 +81,18 @@ class OrderedHashTable : public FixedArray {
// Returns true if the OrderedHashTable contains the key // Returns true if the OrderedHashTable contains the key
static bool HasKey(Isolate* isolate, Derived table, Object 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 // Returns a true value if the OrderedHashTable contains the key and
// the key has been deleted. This does not shrink the table. // the key has been deleted. This does not shrink the table.
static bool Delete(Isolate* isolate, Derived table, Object key); static bool Delete(Isolate* isolate, Derived table, Object key);
InternalIndex FindEntry(Isolate* isolate, Object key); InternalIndex FindEntry(Isolate* isolate, Object key);
Object SlowReverseLookup(Isolate* isolate, Object value);
int NumberOfElements() const { int NumberOfElements() const {
return Smi::ToInt(get(NumberOfElementsIndex())); return Smi::ToInt(get(NumberOfElementsIndex()));
} }
...@@ -109,12 +115,16 @@ class OrderedHashTable : public FixedArray { ...@@ -109,12 +115,16 @@ class OrderedHashTable : public FixedArray {
return InternalIndex::Range(UsedCapacity()); 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) { Object KeyAt(InternalIndex entry) {
DCHECK_LT(entry.as_int(), this->UsedCapacity()); DCHECK_LT(entry.as_int(), this->UsedCapacity());
return get(EntryToIndex(entry)); 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(); } bool IsObsolete() { return !get(NextTableIndex()).IsSmi(); }
// The next newer table. This is only valid if the table is obsolete. // The next newer table. This is only valid if the table is obsolete.
...@@ -133,7 +143,7 @@ class OrderedHashTable : public FixedArray { ...@@ -133,7 +143,7 @@ class OrderedHashTable : public FixedArray {
static const int kNotFound = -1; static const int kNotFound = -1;
// The minimum capacity. Note that despite this value, 0 is also a permitted // The minimum capacity. Note that despite this value, 0 is also a permitted
// capacity, indicating a table without any storage for elements. // 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; } static constexpr int PrefixIndex() { return 0; }
...@@ -752,6 +762,10 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary ...@@ -752,6 +762,10 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary
InternalIndex FindEntry(Isolate* isolate, Object key); InternalIndex FindEntry(Isolate* isolate, Object key);
int NumberOfEnumerableProperties();
Object SlowReverseLookup(Isolate* isolate, Object value);
static Handle<OrderedNameDictionary> DeleteEntry( static Handle<OrderedNameDictionary> DeleteEntry(
Isolate* isolate, Handle<OrderedNameDictionary> table, Isolate* isolate, Handle<OrderedNameDictionary> table,
InternalIndex entry); InternalIndex entry);
...@@ -769,6 +783,9 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary ...@@ -769,6 +783,9 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary
// Returns the value for entry. // Returns the value for entry.
inline Object ValueAt(InternalIndex entry); inline Object ValueAt(InternalIndex entry);
// Like KeyAt, but casts to Name
inline Name NameAt(InternalIndex entry);
// Set the value for entry. // Set the value for entry.
inline void ValueAtPut(InternalIndex entry, Object value); inline void ValueAtPut(InternalIndex entry, Object value);
...@@ -789,6 +806,8 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary ...@@ -789,6 +806,8 @@ class V8_EXPORT_PRIVATE OrderedNameDictionary
static const int kPropertyDetailsOffset = 2; static const int kPropertyDetailsOffset = 2;
static const int kPrefixSize = 1; static const int kPrefixSize = 1;
static const bool kIsOrderedDictionaryType = true;
OBJECT_CONSTRUCTORS(OrderedNameDictionary, OBJECT_CONSTRUCTORS(OrderedNameDictionary,
OrderedHashTable<OrderedNameDictionary, 3>); OrderedHashTable<OrderedNameDictionary, 3>);
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment