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

[dict-proto] Move CollectKeys, CopyEnumKeysTo, and EnumIndexComparator

This turns the member functions CollectKeysTo and CopyEnumKeysTo of
BaseNameDictionary into helper function in keys.cc; they are only used
there. Further, CollectKeysTo is renamed to CollectKeysFromDictionary.

EnumIndexComparator is moved from keys.cc to dictionary.h.

All moves are motivated by
https://chromium-review.googlesource.com/c/v8/v8/+/2489692 needing
these function in other places.

Bug: v8:7569
Change-Id: Ia8039e98fd00cef45dec376f3c401635b2321761
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2509597Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Frank Emrich <emrich@google.com>
Cr-Commit-Position: refs/heads/master@{#70912}
parent 14570fe0
...@@ -141,11 +141,6 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary ...@@ -141,11 +141,6 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary
AllocationType allocation = AllocationType::kYoung, AllocationType allocation = AllocationType::kYoung,
MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY); MinimumCapacity capacity_option = USE_DEFAULT_MINIMUM_CAPACITY);
// Collect the keys into the given KeyAccumulator, in ascending chronological
// order of property creation.
V8_WARN_UNUSED_RESULT static ExceptionStatus CollectKeysTo(
Handle<Derived> dictionary, KeyAccumulator* keys);
// Allocate the next enumeration index. Possibly updates all enumeration // Allocate the next enumeration index. Possibly updates all enumeration
// indices in the table. // indices in the table.
static int NextEnumerationIndex(Isolate* isolate, Handle<Derived> dictionary); static int NextEnumerationIndex(Isolate* isolate, Handle<Derived> dictionary);
...@@ -157,13 +152,6 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary ...@@ -157,13 +152,6 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) BaseNameDictionary
static Handle<FixedArray> IterationIndices(Isolate* isolate, static Handle<FixedArray> IterationIndices(Isolate* isolate,
Handle<Derived> dictionary); Handle<Derived> dictionary);
// Copies enumerable keys to preallocated fixed array.
// Does not throw for uninitialized exports in module namespace objects, so
// this has to be checked separately.
static void CopyEnumKeysTo(Isolate* isolate, Handle<Derived> dictionary,
Handle<FixedArray> storage, KeyCollectionMode mode,
KeyAccumulator* accumulator);
template <typename LocalIsolate> template <typename LocalIsolate>
V8_WARN_UNUSED_RESULT static Handle<Derived> AddNoUpdateNextEnumerationIndex( V8_WARN_UNUSED_RESULT static Handle<Derived> AddNoUpdateNextEnumerationIndex(
LocalIsolate* isolate, Handle<Derived> dictionary, Key key, LocalIsolate* isolate, Handle<Derived> dictionary, Key key,
...@@ -361,6 +349,22 @@ class NumberDictionary ...@@ -361,6 +349,22 @@ class NumberDictionary
Dictionary<NumberDictionary, NumberDictionaryShape>); Dictionary<NumberDictionary, NumberDictionaryShape>);
}; };
// The comparator is passed two indices |a| and |b|, and it returns < 0 when the
// property at index |a| comes before the property at index |b| in the
// enumeration order.
template <typename Dictionary>
struct EnumIndexComparator {
explicit EnumIndexComparator(Dictionary dict) : dict(dict) {}
bool operator()(Tagged_t a, Tagged_t b) {
PropertyDetails da(
dict.DetailsAt(InternalIndex(Smi(static_cast<Address>(a)).value())));
PropertyDetails db(
dict.DetailsAt(InternalIndex(Smi(static_cast<Address>(b)).value())));
return da.dictionary_index() < db.dictionary_index();
}
Dictionary dict;
};
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "src/objects/ordered-hash-table-inl.h" #include "src/objects/ordered-hash-table-inl.h"
#include "src/objects/property-descriptor.h" #include "src/objects/property-descriptor.h"
#include "src/objects/prototype.h" #include "src/objects/prototype.h"
#include "src/objects/slots-atomic-inl.h"
#include "src/utils/identity-map.h" #include "src/utils/identity-map.h"
#include "src/zone/zone-hashmap.h" #include "src/zone/zone-hashmap.h"
...@@ -811,6 +812,61 @@ base::Optional<int> CollectOwnPropertyNamesInternal( ...@@ -811,6 +812,61 @@ base::Optional<int> CollectOwnPropertyNamesInternal(
return first_skipped; 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) {
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;
} else {
storage->set(properties, Smi::FromInt(i.as_int()));
}
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));
}
}
}
template <class T> template <class T>
Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate, Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
KeyCollectionMode mode, KeyCollectionMode mode,
...@@ -823,9 +879,77 @@ Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate, ...@@ -823,9 +879,77 @@ Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
} }
int length = dictionary->NumberOfEnumerableProperties(); int length = dictionary->NumberOfEnumerableProperties();
Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length); Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
T::CopyEnumKeysTo(isolate, dictionary, storage, mode, accumulator); CopyEnumKeysTo(isolate, dictionary, storage, mode, accumulator);
return storage; return storage;
} }
// Collect the keys from |dictionary| into |keys|, in ascending chronological
// order of property creation.
template <typename Dictionary>
ExceptionStatus CollectKeysFromDictionary(Handle<Dictionary> dictionary,
KeyAccumulator* keys) {
Isolate* isolate = keys->isolate();
ReadOnlyRoots roots(isolate);
// TODO(jkummerow): Consider using a std::unique_ptr<InternalIndex[]> instead.
Handle<FixedArray> array =
isolate->factory()->NewFixedArray(dictionary->NumberOfElements());
int array_size = 0;
PropertyFilter filter = keys->filter();
// Handle enumerable strings in CopyEnumKeysTo.
DCHECK_NE(keys->filter(), ENUMERABLE_STRINGS);
{
DisallowHeapAllocation no_gc;
for (InternalIndex i : dictionary->IterateEntries()) {
Object key;
Dictionary raw_dictionary = *dictionary;
if (!raw_dictionary.ToKey(roots, i, &key)) continue;
if (key.FilterKey(filter)) continue;
PropertyDetails details = raw_dictionary.DetailsAt(i);
if ((details.attributes() & filter) != 0) {
AllowHeapAllocation gc;
// This might allocate, but {key} is not used afterwards.
keys->AddShadowingKey(key, &gc);
continue;
}
if (filter & ONLY_ALL_CAN_READ) {
if (details.kind() != kAccessor) continue;
Object accessors = raw_dictionary.ValueAt(i);
if (!accessors.IsAccessorInfo()) continue;
if (!AccessorInfo::cast(accessors).all_can_read()) continue;
}
array->set(array_size++, Smi::FromInt(i.as_int()));
}
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;
for (int i = 0; i < array_size; i++) {
InternalIndex index(Smi::ToInt(array->get(i)));
Object key = dictionary->NameAt(index);
if (key.IsSymbol()) {
has_seen_symbol = true;
continue;
}
ExceptionStatus status = keys->AddKey(key, DO_NOT_CONVERT);
if (!status) return status;
}
if (has_seen_symbol) {
for (int i = 0; i < array_size; i++) {
InternalIndex index(Smi::ToInt(array->get(i)));
Object key = dictionary->NameAt(index);
if (!key.IsSymbol()) continue;
ExceptionStatus status = keys->AddKey(key, DO_NOT_CONVERT);
if (!status) return status;
}
}
return ExceptionStatus::kSuccess;
}
} // namespace } // namespace
Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
...@@ -886,11 +1010,11 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, ...@@ -886,11 +1010,11 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
object, this, descs, first_symbol.value(), limit)); object, this, descs, first_symbol.value(), limit));
} }
} else if (object->IsJSGlobalObject()) { } else if (object->IsJSGlobalObject()) {
RETURN_NOTHING_IF_NOT_SUCCESSFUL(GlobalDictionary::CollectKeysTo( RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_), handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_),
this)); this));
} else { } else {
RETURN_NOTHING_IF_NOT_SUCCESSFUL(NameDictionary::CollectKeysTo( RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(object->property_dictionary(), isolate_), this)); handle(object->property_dictionary(), isolate_), this));
} }
} }
...@@ -907,11 +1031,11 @@ ExceptionStatus KeyAccumulator::CollectPrivateNames(Handle<JSReceiver> receiver, ...@@ -907,11 +1031,11 @@ ExceptionStatus KeyAccumulator::CollectPrivateNames(Handle<JSReceiver> receiver,
object->map().instance_descriptors(kRelaxedLoad), isolate_); object->map().instance_descriptors(kRelaxedLoad), isolate_);
CollectOwnPropertyNamesInternal<false>(object, this, descs, 0, limit); CollectOwnPropertyNamesInternal<false>(object, this, descs, 0, limit);
} else if (object->IsJSGlobalObject()) { } else if (object->IsJSGlobalObject()) {
RETURN_FAILURE_IF_NOT_SUCCESSFUL(GlobalDictionary::CollectKeysTo( RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_), handle(JSGlobalObject::cast(*object).global_dictionary(), isolate_),
this)); this));
} else { } else {
RETURN_FAILURE_IF_NOT_SUCCESSFUL(NameDictionary::CollectKeysTo( RETURN_FAILURE_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(object->property_dictionary(), isolate_), this)); handle(object->property_dictionary(), isolate_), this));
} }
return ExceptionStatus::kSuccess; return ExceptionStatus::kSuccess;
...@@ -1022,7 +1146,7 @@ Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver, ...@@ -1022,7 +1146,7 @@ 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(NameDictionary::CollectKeysTo( RETURN_NOTHING_IF_NOT_SUCCESSFUL(CollectKeysFromDictionary(
handle(proxy->property_dictionary(), isolate_), this)); handle(proxy->property_dictionary(), isolate_), this));
return Just(true); return Just(true);
} }
......
...@@ -6454,71 +6454,6 @@ int Dictionary<Derived, Shape>::NumberOfEnumerableProperties() { ...@@ -6454,71 +6454,6 @@ int Dictionary<Derived, Shape>::NumberOfEnumerableProperties() {
return result; return result;
} }
template <typename Dictionary>
struct EnumIndexComparator {
explicit EnumIndexComparator(Dictionary dict) : dict(dict) {}
bool operator()(Tagged_t a, Tagged_t b) {
PropertyDetails da(
dict.DetailsAt(InternalIndex(Smi(static_cast<Address>(a)).value())));
PropertyDetails db(
dict.DetailsAt(InternalIndex(Smi(static_cast<Address>(b)).value())));
return da.dictionary_index() < db.dictionary_index();
}
Dictionary dict;
};
template <typename Derived, typename Shape>
void BaseNameDictionary<Derived, Shape>::CopyEnumKeysTo(
Isolate* isolate, Handle<Derived> 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;
} else {
storage->set(properties, Smi::FromInt(i.as_int()));
}
properties++;
if (mode == KeyCollectionMode::kOwnOnly && properties == length) break;
}
}
CHECK_EQ(length, properties);
{
DisallowHeapAllocation no_gc;
Derived raw_dictionary = *dictionary;
FixedArray raw_storage = *storage;
EnumIndexComparator<Derived> 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 <typename Derived, typename Shape> template <typename Derived, typename Shape>
Handle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices( Handle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices(
Isolate* isolate, Handle<Derived> dictionary) { Isolate* isolate, Handle<Derived> dictionary) {
...@@ -6552,71 +6487,6 @@ Handle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices( ...@@ -6552,71 +6487,6 @@ Handle<FixedArray> BaseNameDictionary<Derived, Shape>::IterationIndices(
return FixedArray::ShrinkOrEmpty(isolate, array, array_size); return FixedArray::ShrinkOrEmpty(isolate, array, array_size);
} }
template <typename Derived, typename Shape>
ExceptionStatus BaseNameDictionary<Derived, Shape>::CollectKeysTo(
Handle<Derived> dictionary, KeyAccumulator* keys) {
Isolate* isolate = keys->isolate();
ReadOnlyRoots roots(isolate);
// TODO(jkummerow): Consider using a std::unique_ptr<InternalIndex[]> instead.
Handle<FixedArray> array =
isolate->factory()->NewFixedArray(dictionary->NumberOfElements());
int array_size = 0;
PropertyFilter filter = keys->filter();
// Handle enumerable strings in CopyEnumKeysTo.
DCHECK_NE(keys->filter(), ENUMERABLE_STRINGS);
{
DisallowHeapAllocation no_gc;
for (InternalIndex i : dictionary->IterateEntries()) {
Object key;
Derived raw_dictionary = *dictionary;
if (!raw_dictionary.ToKey(roots, i, &key)) continue;
if (key.FilterKey(filter)) continue;
PropertyDetails details = raw_dictionary.DetailsAt(i);
if ((details.attributes() & filter) != 0) {
AllowHeapAllocation gc;
// This might allocate, but {key} is not used afterwards.
keys->AddShadowingKey(key, &gc);
continue;
}
if (filter & ONLY_ALL_CAN_READ) {
if (details.kind() != kAccessor) continue;
Object accessors = raw_dictionary.ValueAt(i);
if (!accessors.IsAccessorInfo()) continue;
if (!AccessorInfo::cast(accessors).all_can_read()) continue;
}
array->set(array_size++, Smi::FromInt(i.as_int()));
}
EnumIndexComparator<Derived> 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;
for (int i = 0; i < array_size; i++) {
InternalIndex index(Smi::ToInt(array->get(i)));
Object key = dictionary->NameAt(index);
if (key.IsSymbol()) {
has_seen_symbol = true;
continue;
}
ExceptionStatus status = keys->AddKey(key, DO_NOT_CONVERT);
if (!status) return status;
}
if (has_seen_symbol) {
for (int i = 0; i < array_size; i++) {
InternalIndex index(Smi::ToInt(array->get(i)));
Object key = dictionary->NameAt(index);
if (!key.IsSymbol()) continue;
ExceptionStatus status = keys->AddKey(key, DO_NOT_CONVERT);
if (!status) return status;
}
}
return ExceptionStatus::kSuccess;
}
// Backwards lookup (slow). // Backwards lookup (slow).
template <typename Derived, typename Shape> template <typename Derived, typename Shape>
Object Dictionary<Derived, Shape>::SlowReverseLookup(Object value) { Object Dictionary<Derived, Shape>::SlowReverseLookup(Object value) {
......
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