[key-accumulator] Starting to reimplement the key-accumulator
Introducing the KeyAccumulator accidentally removed some crucial fast-paths. This CL starts rewriting the KeyAccumulator, step-by-step introducing the special cases again. BUG=chromium:545503, v8:4758 LOG=y Committed: https://crrev.com/9c61327ecb2ee41f34232632e0cac93202bae6b7 Cr-Commit-Position: refs/heads/master@{#34532} Review URL: https://codereview.chromium.org/1707743002 Cr-Commit-Position: refs/heads/master@{#34548}
This commit is contained in:
parent
e99d292909
commit
b954c872aa
4
BUILD.gn
4
BUILD.gn
@ -1139,8 +1139,8 @@ source_set("v8_base") {
|
||||
"src/isolate.h",
|
||||
"src/json-parser.h",
|
||||
"src/json-stringifier.h",
|
||||
"src/key-accumulator.h",
|
||||
"src/key-accumulator.cc",
|
||||
"src/keys.h",
|
||||
"src/keys.cc",
|
||||
"src/layout-descriptor-inl.h",
|
||||
"src/layout-descriptor.cc",
|
||||
"src/layout-descriptor.h",
|
||||
|
204
src/elements.cc
204
src/elements.cc
@ -428,7 +428,6 @@ static void CopyDictionaryToDoubleElements(FixedArrayBase* from_base,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void TraceTopFrame(Isolate* isolate) {
|
||||
StackFrameIterator it(isolate);
|
||||
if (it.done()) {
|
||||
@ -730,6 +729,16 @@ class ElementsAccessorBase : public ElementsAccessor {
|
||||
JSObject::ValidateElements(array);
|
||||
}
|
||||
|
||||
static uint32_t GetIterationLength(JSObject* receiver,
|
||||
FixedArrayBase* elements) {
|
||||
if (receiver->IsJSArray()) {
|
||||
return static_cast<uint32_t>(
|
||||
Smi::cast(JSArray::cast(receiver)->length())->value());
|
||||
} else {
|
||||
return ElementsAccessorSubclass::GetCapacityImpl(receiver, elements);
|
||||
}
|
||||
}
|
||||
|
||||
static Handle<FixedArrayBase> ConvertElementsWithCapacity(
|
||||
Handle<JSObject> object, Handle<FixedArrayBase> old_elements,
|
||||
ElementsKind from_kind, uint32_t capacity) {
|
||||
@ -846,6 +855,14 @@ class ElementsAccessorBase : public ElementsAccessor {
|
||||
from, from_start, *to, from_kind, to_start, packed_size, copy_size);
|
||||
}
|
||||
|
||||
void CollectElementIndices(Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store,
|
||||
KeyAccumulator* keys, uint32_t range,
|
||||
PropertyFilter filter, uint32_t offset) final {
|
||||
ElementsAccessorSubclass::CollectElementIndicesImpl(
|
||||
object, backing_store, keys, range, filter, offset);
|
||||
}
|
||||
|
||||
static void CollectElementIndicesImpl(Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store,
|
||||
KeyAccumulator* keys, uint32_t range,
|
||||
@ -856,30 +873,102 @@ class ElementsAccessorBase : public ElementsAccessor {
|
||||
// Non-dictionary elements can't have all-can-read accessors.
|
||||
return;
|
||||
}
|
||||
uint32_t length = 0;
|
||||
if (object->IsJSArray()) {
|
||||
length = Smi::cast(JSArray::cast(*object)->length())->value();
|
||||
} else {
|
||||
length =
|
||||
ElementsAccessorSubclass::GetCapacityImpl(*object, *backing_store);
|
||||
}
|
||||
uint32_t length = GetIterationLength(*object, *backing_store);
|
||||
if (range < length) length = range;
|
||||
for (uint32_t i = offset; i < length; i++) {
|
||||
if (!ElementsAccessorSubclass::HasElementImpl(object, i, backing_store,
|
||||
filter)) {
|
||||
continue;
|
||||
if (ElementsAccessorSubclass::HasElementImpl(object, i, backing_store,
|
||||
filter)) {
|
||||
keys->AddKey(i);
|
||||
}
|
||||
keys->AddKey(i);
|
||||
}
|
||||
}
|
||||
|
||||
void CollectElementIndices(Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store,
|
||||
KeyAccumulator* keys, uint32_t range,
|
||||
PropertyFilter filter, uint32_t offset) final {
|
||||
ElementsAccessorSubclass::CollectElementIndicesImpl(
|
||||
object, backing_store, keys, range, filter, offset);
|
||||
};
|
||||
static Handle<FixedArray> DirectCollectElementIndicesImpl(
|
||||
Isolate* isolate, Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
|
||||
PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices) {
|
||||
uint32_t length =
|
||||
ElementsAccessorSubclass::GetIterationLength(*object, *backing_store);
|
||||
uint32_t insertion_index = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (ElementsAccessorSubclass::HasElementImpl(object, i, backing_store,
|
||||
filter)) {
|
||||
if (convert == CONVERT_TO_STRING) {
|
||||
list->set(insertion_index, *isolate->factory()->Uint32ToString(i));
|
||||
} else {
|
||||
list->set(insertion_index, Smi::FromInt(i), SKIP_WRITE_BARRIER);
|
||||
}
|
||||
insertion_index++;
|
||||
}
|
||||
}
|
||||
*nof_indices = insertion_index;
|
||||
return list;
|
||||
}
|
||||
|
||||
Handle<FixedArray> PrependElementIndices(Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store,
|
||||
Handle<FixedArray> keys,
|
||||
GetKeysConversion convert,
|
||||
PropertyFilter filter) final {
|
||||
return ElementsAccessorSubclass::PrependElementIndicesImpl(
|
||||
object, backing_store, keys, convert, filter);
|
||||
}
|
||||
|
||||
static Handle<FixedArray> PrependElementIndicesImpl(
|
||||
Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
|
||||
Handle<FixedArray> keys, GetKeysConversion convert,
|
||||
PropertyFilter filter) {
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
uint32_t nof_property_keys = keys->length();
|
||||
uint32_t initial_list_length =
|
||||
ElementsAccessorSubclass::GetCapacityImpl(*object, *backing_store);
|
||||
initial_list_length += nof_property_keys;
|
||||
|
||||
// Collect the element indices into a new list.
|
||||
uint32_t nof_indices = 0;
|
||||
Handle<FixedArray> combined_keys =
|
||||
isolate->factory()->NewFixedArray(initial_list_length);
|
||||
combined_keys = ElementsAccessorSubclass::DirectCollectElementIndicesImpl(
|
||||
isolate, object, backing_store, convert, filter, combined_keys,
|
||||
&nof_indices);
|
||||
|
||||
// Sort the indices list if necessary.
|
||||
if (IsDictionaryElementsKind(kind())) {
|
||||
struct {
|
||||
bool operator()(Object* a, Object* b) {
|
||||
if (!a->IsUndefined()) {
|
||||
if (b->IsUndefined()) return true;
|
||||
return a->Number() < b->Number();
|
||||
}
|
||||
return !b->IsUndefined();
|
||||
}
|
||||
} cmp;
|
||||
Object** start =
|
||||
reinterpret_cast<Object**>(combined_keys->GetFirstElementAddress());
|
||||
std::sort(start, start + nof_indices, cmp);
|
||||
// Indices from dictionary elements should only be converted after
|
||||
// sorting.
|
||||
if (convert == CONVERT_TO_STRING) {
|
||||
for (uint32_t i = 0; i < nof_indices; i++) {
|
||||
combined_keys->set(i, *isolate->factory()->Uint32ToString(
|
||||
combined_keys->get(i)->Number()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over the passed-in property keys.
|
||||
CopyObjectToObjectElements(*keys, FAST_ELEMENTS, 0, *combined_keys,
|
||||
FAST_ELEMENTS, nof_indices, nof_property_keys);
|
||||
|
||||
if (IsHoleyElementsKind(kind())) {
|
||||
// Shrink combined_keys to the final size.
|
||||
int final_size = nof_indices + nof_property_keys;
|
||||
DCHECK_LE(final_size, combined_keys->length());
|
||||
combined_keys->Shrink(final_size);
|
||||
}
|
||||
|
||||
return combined_keys;
|
||||
}
|
||||
|
||||
void AddElementsToKeyAccumulator(Handle<JSObject> receiver,
|
||||
KeyAccumulator* accumulator,
|
||||
@ -912,12 +1001,7 @@ class ElementsAccessorBase : public ElementsAccessor {
|
||||
? index
|
||||
: kMaxUInt32;
|
||||
} else {
|
||||
uint32_t length =
|
||||
holder->IsJSArray()
|
||||
? static_cast<uint32_t>(
|
||||
Smi::cast(JSArray::cast(holder)->length())->value())
|
||||
: ElementsAccessorSubclass::GetCapacityImpl(holder,
|
||||
backing_store);
|
||||
uint32_t length = GetIterationLength(holder, backing_store);
|
||||
return index < length ? index : kMaxUInt32;
|
||||
}
|
||||
}
|
||||
@ -1119,6 +1203,28 @@ class DictionaryElementsAccessor
|
||||
return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry);
|
||||
}
|
||||
|
||||
static uint32_t GetKeyForEntryImpl(Handle<SeededNumberDictionary> dictionary,
|
||||
int entry, PropertyFilter filter) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Object* raw_key = dictionary->KeyAt(entry);
|
||||
if (!dictionary->IsKey(raw_key)) return kMaxUInt32;
|
||||
if (raw_key->FilterKey(filter)) return kMaxUInt32;
|
||||
if (dictionary->IsDeleted(entry)) return kMaxUInt32;
|
||||
DCHECK(raw_key->IsNumber());
|
||||
DCHECK_LE(raw_key->Number(), kMaxUInt32);
|
||||
uint32_t key = static_cast<uint32_t>(raw_key->Number());
|
||||
PropertyDetails details = dictionary->DetailsAt(entry);
|
||||
if (filter & ONLY_ALL_CAN_READ) {
|
||||
if (details.kind() != kAccessor) return kMaxUInt32;
|
||||
Object* accessors = dictionary->ValueAt(entry);
|
||||
if (!accessors->IsAccessorInfo()) return kMaxUInt32;
|
||||
if (!AccessorInfo::cast(accessors)->all_can_read()) return kMaxUInt32;
|
||||
}
|
||||
PropertyAttributes attr = details.attributes();
|
||||
if ((attr & filter) != 0) return kMaxUInt32;
|
||||
return key;
|
||||
}
|
||||
|
||||
static void CollectElementIndicesImpl(Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store,
|
||||
KeyAccumulator* keys, uint32_t range,
|
||||
@ -1128,29 +1234,32 @@ class DictionaryElementsAccessor
|
||||
Handle<SeededNumberDictionary>::cast(backing_store);
|
||||
int capacity = dictionary->Capacity();
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
Object* k = dictionary->KeyAt(i);
|
||||
if (!dictionary->IsKey(k)) continue;
|
||||
if (k->FilterKey(filter)) continue;
|
||||
if (dictionary->IsDeleted(i)) continue;
|
||||
DCHECK(k->IsNumber());
|
||||
DCHECK_LE(k->Number(), kMaxUInt32);
|
||||
uint32_t index = static_cast<uint32_t>(k->Number());
|
||||
if (index < offset) continue;
|
||||
PropertyDetails details = dictionary->DetailsAt(i);
|
||||
if (filter & ONLY_ALL_CAN_READ) {
|
||||
if (details.kind() != kAccessor) continue;
|
||||
Object* accessors = dictionary->ValueAt(i);
|
||||
if (!accessors->IsAccessorInfo()) continue;
|
||||
if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
|
||||
}
|
||||
PropertyAttributes attr = details.attributes();
|
||||
if ((attr & filter) != 0) continue;
|
||||
keys->AddKey(index);
|
||||
uint32_t key = GetKeyForEntryImpl(dictionary, i, filter);
|
||||
if (key == kMaxUInt32) continue;
|
||||
keys->AddKey(key);
|
||||
}
|
||||
|
||||
keys->SortCurrentElementsList();
|
||||
}
|
||||
|
||||
static Handle<FixedArray> DirectCollectElementIndicesImpl(
|
||||
Isolate* isolate, Handle<JSObject> object,
|
||||
Handle<FixedArrayBase> backing_store, GetKeysConversion convert,
|
||||
PropertyFilter filter, Handle<FixedArray> list, uint32_t* nof_indices) {
|
||||
Handle<SeededNumberDictionary> dictionary =
|
||||
Handle<SeededNumberDictionary>::cast(backing_store);
|
||||
uint32_t capacity = dictionary->Capacity();
|
||||
uint32_t insertion_index = 0;
|
||||
for (uint32_t i = 0; i < capacity; i++) {
|
||||
uint32_t key = GetKeyForEntryImpl(dictionary, i, filter);
|
||||
if (key == kMaxUInt32) continue;
|
||||
list->set(insertion_index, *isolate->factory()->NewNumberFromUint(key));
|
||||
insertion_index++;
|
||||
}
|
||||
*nof_indices = insertion_index;
|
||||
return list;
|
||||
}
|
||||
|
||||
static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
|
||||
KeyAccumulator* accumulator,
|
||||
AddKeyConversion convert) {
|
||||
@ -1315,15 +1424,10 @@ class FastElementsAccessor
|
||||
static void AddElementsToKeyAccumulatorImpl(Handle<JSObject> receiver,
|
||||
KeyAccumulator* accumulator,
|
||||
AddKeyConversion convert) {
|
||||
uint32_t length = 0;
|
||||
Handle<FixedArrayBase> elements(receiver->elements(),
|
||||
receiver->GetIsolate());
|
||||
if (receiver->IsJSArray()) {
|
||||
length = Smi::cast(JSArray::cast(*receiver)->length())->value();
|
||||
} else {
|
||||
length =
|
||||
FastElementsAccessorSubclass::GetCapacityImpl(*receiver, *elements);
|
||||
}
|
||||
uint32_t length =
|
||||
FastElementsAccessorSubclass::GetIterationLength(*receiver, *elements);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (IsFastPackedElementsKind(KindTraits::Kind) ||
|
||||
HasEntryImpl(*elements, i)) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "src/elements-kind.h"
|
||||
#include "src/heap/heap.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/key-accumulator.h"
|
||||
#include "src/keys.h"
|
||||
#include "src/objects.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -99,6 +99,19 @@ class ElementsAccessor {
|
||||
filter, offset);
|
||||
}
|
||||
|
||||
//
|
||||
virtual Handle<FixedArray> PrependElementIndices(
|
||||
Handle<JSObject> object, Handle<FixedArrayBase> backing_store,
|
||||
Handle<FixedArray> keys, GetKeysConversion convert,
|
||||
PropertyFilter filter = ALL_PROPERTIES) = 0;
|
||||
|
||||
inline Handle<FixedArray> PrependElementIndices(
|
||||
Handle<JSObject> object, Handle<FixedArray> keys,
|
||||
GetKeysConversion convert, PropertyFilter filter = ALL_PROPERTIES) {
|
||||
return PrependElementIndices(object, handle(object->elements()), keys,
|
||||
convert, filter);
|
||||
}
|
||||
|
||||
virtual void AddElementsToKeyAccumulator(Handle<JSObject> receiver,
|
||||
KeyAccumulator* accumulator,
|
||||
AddKeyConversion convert) = 0;
|
||||
|
@ -2,26 +2,24 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/key-accumulator.h"
|
||||
#include "src/keys.h"
|
||||
|
||||
#include "src/elements.h"
|
||||
#include "src/factory.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/property-descriptor.h"
|
||||
|
||||
#include "src/prototype.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
|
||||
KeyAccumulator::~KeyAccumulator() {
|
||||
for (size_t i = 0; i < elements_.size(); i++) {
|
||||
delete elements_[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
|
||||
if (length_ == 0) {
|
||||
return isolate_->factory()->empty_fixed_array();
|
||||
@ -86,7 +84,6 @@ Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
bool AccumulatorHasKey(std::vector<uint32_t>* sub_elements, uint32_t key) {
|
||||
@ -99,7 +96,6 @@ bool KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) {
|
||||
return AddKey(handle(key, isolate_), convert);
|
||||
}
|
||||
|
||||
|
||||
bool KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
|
||||
if (key->IsSymbol()) {
|
||||
if (filter_ & SKIP_SYMBOLS) return false;
|
||||
@ -136,10 +132,8 @@ bool KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
|
||||
return AddStringKey(key, convert);
|
||||
}
|
||||
|
||||
|
||||
bool KeyAccumulator::AddKey(uint32_t key) { return AddIntegerKey(key); }
|
||||
|
||||
|
||||
bool KeyAccumulator::AddIntegerKey(uint32_t key) {
|
||||
// Make sure we do not add keys to a proxy-level (see AddKeysFromProxy).
|
||||
// We mark proxy-levels with a negative length
|
||||
@ -154,7 +148,6 @@ bool KeyAccumulator::AddIntegerKey(uint32_t key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool KeyAccumulator::AddStringKey(Handle<Object> key,
|
||||
AddKeyConversion convert) {
|
||||
if (string_properties_.is_null()) {
|
||||
@ -176,7 +169,6 @@ bool KeyAccumulator::AddStringKey(Handle<Object> key,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool KeyAccumulator::AddSymbolKey(Handle<Object> key) {
|
||||
if (symbol_properties_.is_null()) {
|
||||
symbol_properties_ = OrderedHashSet::Allocate(isolate_, 16);
|
||||
@ -192,7 +184,6 @@ bool KeyAccumulator::AddSymbolKey(Handle<Object> key) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::AddKeys(Handle<FixedArray> array,
|
||||
AddKeyConversion convert) {
|
||||
int add_length = array->length();
|
||||
@ -203,7 +194,6 @@ void KeyAccumulator::AddKeys(Handle<FixedArray> array,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
|
||||
AddKeyConversion convert) {
|
||||
DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements());
|
||||
@ -211,7 +201,6 @@ void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
|
||||
accessor->AddElementsToKeyAccumulator(array_like, this, convert);
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::AddKeysFromProxy(Handle<JSObject> array_like) {
|
||||
// Proxies define a complete list of keys with no distinction of
|
||||
// elements and properties, which breaks the normal assumption for the
|
||||
@ -223,7 +212,6 @@ void KeyAccumulator::AddKeysFromProxy(Handle<JSObject> array_like) {
|
||||
level_string_length_ = -level_string_length_;
|
||||
}
|
||||
|
||||
|
||||
MaybeHandle<FixedArray> FilterProxyKeys(Isolate* isolate, Handle<JSProxy> owner,
|
||||
Handle<FixedArray> keys,
|
||||
PropertyFilter filter) {
|
||||
@ -253,7 +241,6 @@ MaybeHandle<FixedArray> FilterProxyKeys(Isolate* isolate, Handle<JSProxy> owner,
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
||||
// Returns "nothing" in case of exception, "true" on success.
|
||||
Maybe<bool> KeyAccumulator::AddKeysFromProxy(Handle<JSProxy> proxy,
|
||||
Handle<FixedArray> keys) {
|
||||
@ -277,7 +264,6 @@ Maybe<bool> KeyAccumulator::AddKeysFromProxy(Handle<JSProxy> proxy,
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::AddElementKeysFromInterceptor(
|
||||
Handle<JSObject> array_like) {
|
||||
AddKeys(array_like, CONVERT_TO_ARRAY_INDEX);
|
||||
@ -286,7 +272,6 @@ void KeyAccumulator::AddElementKeysFromInterceptor(
|
||||
SortCurrentElementsListRemoveDuplicates();
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::SortCurrentElementsListRemoveDuplicates() {
|
||||
// Sort and remove duplicates from the current elements level and adjust.
|
||||
// the lengths accordingly.
|
||||
@ -300,14 +285,12 @@ void KeyAccumulator::SortCurrentElementsListRemoveDuplicates() {
|
||||
length_ -= static_cast<int>(nof_removed_keys);
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::SortCurrentElementsList() {
|
||||
if (elements_.empty()) return;
|
||||
auto element_keys = elements_.back();
|
||||
std::sort(element_keys->begin(), element_keys->end());
|
||||
}
|
||||
|
||||
|
||||
void KeyAccumulator::NextPrototype() {
|
||||
// Store the protoLength on the first call of this method.
|
||||
if (!elements_.empty()) {
|
||||
@ -319,6 +302,125 @@ void KeyAccumulator::NextPrototype() {
|
||||
level_symbol_length_ = 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void TrySettingEmptyEnumCache(JSReceiver* object) {
|
||||
Map* map = object->map();
|
||||
DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength());
|
||||
if (!map->OnlyHasSimpleProperties()) return;
|
||||
if (map->IsJSProxyMap()) return;
|
||||
if (map->NumberOfOwnDescriptors() > 0) {
|
||||
int number_of_enumerable_own_properties =
|
||||
map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
|
||||
if (number_of_enumerable_own_properties > 0) return;
|
||||
}
|
||||
DCHECK(object->IsJSObject());
|
||||
map->SetEnumLength(0);
|
||||
}
|
||||
|
||||
bool CheckAndInitalizeSimpleEnumCache(JSReceiver* object) {
|
||||
if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
|
||||
TrySettingEmptyEnumCache(object);
|
||||
}
|
||||
if (object->map()->EnumLength() != 0) return false;
|
||||
DCHECK(object->IsJSObject());
|
||||
return !JSObject::cast(object)->HasEnumerableElements();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void FastKeyAccumulator::Prepare() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
// Directly go for the fast path for OWN_ONLY keys.
|
||||
if (type_ == OWN_ONLY) return;
|
||||
// Fully walk the prototype chain and find the last prototype with keys.
|
||||
is_receiver_simple_enum_ = false;
|
||||
has_empty_prototype_ = true;
|
||||
JSReceiver* first_non_empty_prototype;
|
||||
for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
|
||||
iter.Advance()) {
|
||||
JSReceiver* current = iter.GetCurrent<JSReceiver>();
|
||||
if (CheckAndInitalizeSimpleEnumCache(current)) continue;
|
||||
has_empty_prototype_ = false;
|
||||
first_non_empty_prototype = current;
|
||||
// TODO(cbruni): use the first non-empty prototype.
|
||||
USE(first_non_empty_prototype);
|
||||
return;
|
||||
}
|
||||
DCHECK(has_empty_prototype_);
|
||||
is_receiver_simple_enum_ =
|
||||
receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
|
||||
!JSObject::cast(*receiver_)->HasEnumerableElements();
|
||||
}
|
||||
|
||||
namespace {
|
||||
Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
|
||||
Handle<JSObject> object,
|
||||
GetKeysConversion convert) {
|
||||
Handle<FixedArray> keys = JSObject::GetFastEnumPropertyKeys(isolate, object);
|
||||
ElementsAccessor* accessor = object->GetElementsAccessor();
|
||||
return accessor->PrependElementIndices(object, keys, convert,
|
||||
ONLY_ENUMERABLE);
|
||||
}
|
||||
|
||||
MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache(
|
||||
Isolate* isolate, Handle<JSObject> object) {
|
||||
// Uninitalized enum cache
|
||||
Map* map = object->map();
|
||||
if (object->elements() != isolate->heap()->empty_fixed_array() ||
|
||||
object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
|
||||
// Assume that there are elements.
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
|
||||
if (number_of_own_descriptors == 0) {
|
||||
map->SetEnumLength(0);
|
||||
return isolate->factory()->empty_fixed_array();
|
||||
}
|
||||
// We have no elements but possibly enumerable property keys, hence we can
|
||||
// directly initialize the enum cache.
|
||||
return JSObject::GetFastEnumPropertyKeys(isolate, object);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) {
|
||||
Handle<FixedArray> keys;
|
||||
if (GetKeysFast(convert).ToHandle(&keys)) {
|
||||
return keys;
|
||||
}
|
||||
return GetKeysSlow(convert);
|
||||
}
|
||||
|
||||
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
|
||||
GetKeysConversion convert) {
|
||||
bool own_only = has_empty_prototype_ || type_ == OWN_ONLY;
|
||||
if (!own_only || !receiver_->map()->OnlyHasSimpleProperties()) {
|
||||
return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
Handle<FixedArray> keys;
|
||||
DCHECK(receiver_->IsJSObject());
|
||||
Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
|
||||
|
||||
int enum_length = receiver_->map()->EnumLength();
|
||||
if (enum_length == kInvalidEnumCacheSentinel) {
|
||||
// Try initializing the enum cache and return own properties.
|
||||
if (GetOwnKeysWithUninitializedEnumCache(isolate_, object)
|
||||
.ToHandle(&keys)) {
|
||||
is_receiver_simple_enum_ =
|
||||
object->map()->EnumLength() != kInvalidEnumCacheSentinel;
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
// The properties-only case failed because there were probably elements on the
|
||||
// receiver.
|
||||
return GetOwnKeysWithElements(isolate_, object, convert);
|
||||
}
|
||||
|
||||
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
|
||||
GetKeysConversion convert) {
|
||||
return JSReceiver::GetKeys(receiver_, type_, ENUMERABLE_STRINGS);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -86,9 +86,40 @@ class KeyAccumulator final BASE_EMBEDDED {
|
||||
DISALLOW_COPY_AND_ASSIGN(KeyAccumulator);
|
||||
};
|
||||
|
||||
// The FastKeyAccumulator handles the cases where there are no elements on the
|
||||
// prototype chain and forwords the complex/slow cases to the normal
|
||||
// KeyAccumulator.
|
||||
class FastKeyAccumulator {
|
||||
public:
|
||||
FastKeyAccumulator(Isolate* isolate, Handle<JSReceiver> receiver,
|
||||
KeyCollectionType type, PropertyFilter filter)
|
||||
: isolate_(isolate), receiver_(receiver), type_(type), filter_(filter) {
|
||||
Prepare();
|
||||
// TODO(cbruni): pass filter_ directly to the KeyAccumulator.
|
||||
USE(filter_);
|
||||
}
|
||||
|
||||
bool is_receiver_simple_enum() { return is_receiver_simple_enum_; }
|
||||
bool has_empty_prototype() { return has_empty_prototype_; }
|
||||
|
||||
MaybeHandle<FixedArray> GetKeys(GetKeysConversion convert = KEEP_NUMBERS);
|
||||
|
||||
private:
|
||||
void Prepare();
|
||||
MaybeHandle<FixedArray> GetKeysFast(GetKeysConversion convert);
|
||||
MaybeHandle<FixedArray> GetKeysSlow(GetKeysConversion convert);
|
||||
|
||||
Isolate* isolate_;
|
||||
Handle<JSReceiver> receiver_;
|
||||
KeyCollectionType type_;
|
||||
PropertyFilter filter_;
|
||||
bool is_receiver_simple_enum_ = false;
|
||||
bool has_empty_prototype_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FastKeyAccumulator);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
||||
#endif // V8_KEY_ACCUMULATOR_H_
|
@ -455,6 +455,12 @@ void Map::MapPrint(std::ostream& os) { // NOLINT
|
||||
}
|
||||
os << "\n - elements kind: " << ElementsKindToString(elements_kind());
|
||||
os << "\n - unused property fields: " << unused_property_fields();
|
||||
os << "\n - enum length: ";
|
||||
if (EnumLength() == kInvalidEnumCacheSentinel) {
|
||||
os << "invalid";
|
||||
} else {
|
||||
os << EnumLength();
|
||||
}
|
||||
if (is_deprecated()) os << "\n - deprecated_map";
|
||||
if (is_stable()) os << "\n - stable_map";
|
||||
if (is_dictionary_map()) os << "\n - dictionary_map";
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include "src/interpreter/bytecodes.h"
|
||||
#include "src/interpreter/source-position-table.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/key-accumulator.h"
|
||||
#include "src/keys.h"
|
||||
#include "src/list.h"
|
||||
#include "src/log.h"
|
||||
#include "src/lookup.h"
|
||||
@ -8226,7 +8226,9 @@ MaybeHandle<Object> JSReceiver::OrdinaryToPrimitive(
|
||||
|
||||
|
||||
// TODO(cbruni/jkummerow): Consider moving this into elements.cc.
|
||||
bool HasEnumerableElements(JSObject* object) {
|
||||
bool JSObject::HasEnumerableElements() {
|
||||
// TODO(cbruni): cleanup
|
||||
JSObject* object = this;
|
||||
switch (object->GetElementsKind()) {
|
||||
case FAST_SMI_ELEMENTS:
|
||||
case FAST_ELEMENTS:
|
||||
@ -8291,7 +8293,6 @@ bool HasEnumerableElements(JSObject* object) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Tests for the fast common case for property enumeration:
|
||||
// - This object and all prototypes has an enum cache (which means that
|
||||
// it is no proxy, has no interceptors and needs no access checks).
|
||||
@ -8308,7 +8309,7 @@ bool JSReceiver::IsSimpleEnum() {
|
||||
if (current->IsAccessCheckNeeded()) return false;
|
||||
DCHECK(!current->HasNamedInterceptor());
|
||||
DCHECK(!current->HasIndexedInterceptor());
|
||||
if (HasEnumerableElements(current)) return false;
|
||||
if (current->HasEnumerableElements()) return false;
|
||||
if (current != this && enum_length != 0) return false;
|
||||
}
|
||||
return true;
|
||||
@ -8372,10 +8373,9 @@ bool Map::OnlyHasSimpleProperties() {
|
||||
!has_hidden_prototype() && !is_dictionary_map();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
|
||||
Handle<JSObject> object) {
|
||||
// static
|
||||
Handle<FixedArray> JSObject::GetFastEnumPropertyKeys(Isolate* isolate,
|
||||
Handle<JSObject> object) {
|
||||
Handle<Map> map(object->map());
|
||||
bool cache_enum_length = map->OnlyHasSimpleProperties();
|
||||
|
||||
@ -8426,8 +8426,9 @@ Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
PropertyDetails details = descs->GetDetails(i);
|
||||
if (details.IsDontEnum()) continue;
|
||||
Object* key = descs->GetKey(i);
|
||||
if (details.IsDontEnum() || key->IsSymbol()) continue;
|
||||
if (key->IsSymbol()) continue;
|
||||
storage->set(index, key);
|
||||
if (!indices.is_null()) {
|
||||
if (details.type() != DATA) {
|
||||
@ -8449,7 +8450,6 @@ Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
|
||||
return storage;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object) {
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
@ -10665,7 +10665,6 @@ Handle<ArrayList> ArrayList::Add(Handle<ArrayList> array, Handle<Object> obj,
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
Handle<ArrayList> ArrayList::Add(Handle<ArrayList> array, Handle<Object> obj1,
|
||||
Handle<Object> obj2, AddMode mode) {
|
||||
int length = array->Length();
|
||||
@ -16427,7 +16426,6 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JSObject::CollectOwnPropertyNames(KeyAccumulator* keys,
|
||||
PropertyFilter filter) {
|
||||
if (HasFastProperties()) {
|
||||
@ -16468,7 +16466,6 @@ int JSObject::NumberOfOwnElements(PropertyFilter filter) {
|
||||
return GetOwnElementKeys(NULL, filter);
|
||||
}
|
||||
|
||||
|
||||
void JSObject::CollectOwnElementKeys(Handle<JSObject> object,
|
||||
KeyAccumulator* keys,
|
||||
PropertyFilter filter) {
|
||||
@ -18476,7 +18473,6 @@ int Dictionary<Derived, Shape, Key>::CopyKeysTo(
|
||||
return index - start_index;
|
||||
}
|
||||
|
||||
|
||||
template <typename Derived, typename Shape, typename Key>
|
||||
void Dictionary<Derived, Shape, Key>::CollectKeysTo(
|
||||
Handle<Dictionary<Derived, Shape, Key> > dictionary, KeyAccumulator* keys,
|
||||
|
@ -2043,6 +2043,8 @@ class JSObject: public JSReceiver {
|
||||
inline bool HasSlowArgumentsElements();
|
||||
inline bool HasFastStringWrapperElements();
|
||||
inline bool HasSlowStringWrapperElements();
|
||||
bool HasEnumerableElements();
|
||||
|
||||
inline SeededNumberDictionary* element_dictionary(); // Gets slow elements.
|
||||
|
||||
// Requires: HasFastElements().
|
||||
@ -2292,6 +2294,9 @@ class JSObject: public JSReceiver {
|
||||
|
||||
static Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object);
|
||||
|
||||
static Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
|
||||
Handle<JSObject> object);
|
||||
|
||||
// Returns a new map with all transitions dropped from the object's current
|
||||
// map and the ElementsKind set.
|
||||
static Handle<Map> GetElementsTransitionMap(Handle<JSObject> object,
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "src/elements.h"
|
||||
#include "src/factory.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/key-accumulator.h"
|
||||
#include "src/keys.h"
|
||||
#include "src/messages.h"
|
||||
#include "src/prototype.h"
|
||||
|
||||
|
@ -5,8 +5,10 @@
|
||||
#include "src/runtime/runtime-utils.h"
|
||||
|
||||
#include "src/arguments.h"
|
||||
#include "src/elements.h"
|
||||
#include "src/factory.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/keys.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -20,15 +22,15 @@ namespace {
|
||||
// deletions during a for-in.
|
||||
MaybeHandle<HeapObject> Enumerate(Handle<JSReceiver> receiver) {
|
||||
Isolate* const isolate = receiver->GetIsolate();
|
||||
FastKeyAccumulator accumulator(isolate, receiver, INCLUDE_PROTOS,
|
||||
ENUMERABLE_STRINGS);
|
||||
// Test if we have an enum cache for {receiver}.
|
||||
if (!receiver->IsSimpleEnum()) {
|
||||
if (!accumulator.is_receiver_simple_enum()) {
|
||||
Handle<FixedArray> keys;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, keys,
|
||||
JSReceiver::GetKeys(receiver, INCLUDE_PROTOS, ENUMERABLE_STRINGS),
|
||||
HeapObject);
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, keys, accumulator.GetKeys(KEEP_NUMBERS),
|
||||
HeapObject);
|
||||
// Test again, since cache may have been built by GetKeys() calls above.
|
||||
if (!receiver->IsSimpleEnum()) return keys;
|
||||
if (!accumulator.is_receiver_simple_enum()) return keys;
|
||||
}
|
||||
return handle(receiver->map(), isolate);
|
||||
}
|
||||
@ -36,10 +38,19 @@ MaybeHandle<HeapObject> Enumerate(Handle<JSReceiver> receiver) {
|
||||
|
||||
MaybeHandle<Object> Filter(Handle<JSReceiver> receiver, Handle<Object> key) {
|
||||
Isolate* const isolate = receiver->GetIsolate();
|
||||
// TODO(turbofan): Fast case for array indices.
|
||||
Handle<Name> name;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, name, Object::ToName(isolate, key),
|
||||
Object);
|
||||
// Directly check for elements if the key is a smi and avoid a conversion
|
||||
// roundtrip (Number -> Name -> Number).
|
||||
if (key->IsNumber() && receiver->map()->OnlyHasSimpleProperties()) {
|
||||
Handle<JSObject> object = Handle<JSObject>::cast(receiver);
|
||||
ElementsAccessor* accessor = object->GetElementsAccessor();
|
||||
DCHECK_LT(key->Number(), kMaxUInt32);
|
||||
if (accessor->HasElement(object, key->Number(), ONLY_ENUMERABLE)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
Maybe<bool> result = JSReceiver::HasProperty(receiver, name);
|
||||
MAYBE_RETURN_NULL(result);
|
||||
if (result.FromJust()) return name;
|
||||
|
@ -120,7 +120,23 @@ for (i=0 ; i < 3; ++i) {
|
||||
assertEquals("undefined", typeof y[0], "y[0]");
|
||||
}
|
||||
|
||||
(function() {
|
||||
(function testLargeElementKeys() {
|
||||
// Key out of SMI range but well within safe double representaion.
|
||||
var large_key = 2147483650;
|
||||
var o = [];
|
||||
// Trigger dictionary elements with HeapNumber keys.
|
||||
o[large_key] = 0;
|
||||
o[large_key+1] = 1;
|
||||
o[large_key+2] = 2;
|
||||
o[large_key+3] = 3;
|
||||
var keys = [];
|
||||
for (var k in o) {
|
||||
keys.push(k);
|
||||
}
|
||||
assertEquals(["2147483650", "2147483651", "2147483652", "2147483653"], keys);
|
||||
})();
|
||||
|
||||
(function testLargeElementKeysWithProto() {
|
||||
var large_key = 2147483650;
|
||||
var o = {__proto__: {}};
|
||||
o[large_key] = 1;
|
||||
@ -131,3 +147,17 @@ for (i=0 ; i < 3; ++i) {
|
||||
}
|
||||
assertEquals(["2147483650"], keys);
|
||||
})();
|
||||
|
||||
(function testNonEnumerableArgumentsIndex() {
|
||||
Object.defineProperty(arguments, 0, {enumerable:false});
|
||||
for (var k in arguments) {
|
||||
assertUnreachable();
|
||||
}
|
||||
})();
|
||||
|
||||
(function testNonEnumerableSloppyArgumentsIndex(a) {
|
||||
Object.defineProperty(arguments, 0, {enumerable:false});
|
||||
for (var k in arguments) {
|
||||
assertUnreachable();
|
||||
}
|
||||
})(true);
|
||||
|
@ -957,8 +957,8 @@
|
||||
'../../src/isolate.h',
|
||||
'../../src/json-parser.h',
|
||||
'../../src/json-stringifier.h',
|
||||
'../../src/key-accumulator.h',
|
||||
'../../src/key-accumulator.cc',
|
||||
'../../src/keys.h',
|
||||
'../../src/keys.cc',
|
||||
'../../src/layout-descriptor-inl.h',
|
||||
'../../src/layout-descriptor.cc',
|
||||
'../../src/layout-descriptor.h',
|
||||
|
Loading…
Reference in New Issue
Block a user