Commit df8970a8 authored by cbruni's avatar cbruni Committed by Commit bot

[runtime] Fancify KeyAccumulator

Separately collect element keys from property keys to avoid slow
corner-cases. Partly deal with keys generated by Proxies.

BUG=chromium:536790
LOG=N

Review URL: https://codereview.chromium.org/1397063002

Cr-Commit-Position: refs/heads/master@{#31378}
parent 16962756
...@@ -518,7 +518,8 @@ class ElementsAccessorBase : public ElementsAccessor { ...@@ -518,7 +518,8 @@ class ElementsAccessorBase : public ElementsAccessor {
uint32_t end) { uint32_t end) {
if (IsFastPackedElementsKind(kind())) return true; if (IsFastPackedElementsKind(kind())) return true;
for (uint32_t i = start; i < end; i++) { for (uint32_t i = start; i < end; i++) {
if (!ElementsAccessorSubclass::HasElementImpl(holder, i, backing_store)) { if (!ElementsAccessorSubclass::HasElementImpl(holder, i, backing_store,
NONE)) {
return false; return false;
} }
} }
...@@ -544,15 +545,17 @@ class ElementsAccessorBase : public ElementsAccessor { ...@@ -544,15 +545,17 @@ class ElementsAccessorBase : public ElementsAccessor {
} }
virtual bool HasElement(Handle<JSObject> holder, uint32_t index, virtual bool HasElement(Handle<JSObject> holder, uint32_t index,
Handle<FixedArrayBase> backing_store) final { Handle<FixedArrayBase> backing_store,
PropertyAttributes filter) final {
return ElementsAccessorSubclass::HasElementImpl(holder, index, return ElementsAccessorSubclass::HasElementImpl(holder, index,
backing_store); backing_store, filter);
} }
static bool HasElementImpl(Handle<JSObject> holder, uint32_t index, static bool HasElementImpl(Handle<JSObject> holder, uint32_t index,
Handle<FixedArrayBase> backing_store) { Handle<FixedArrayBase> backing_store,
PropertyAttributes filter) {
return ElementsAccessorSubclass::GetEntryForIndexImpl( return ElementsAccessorSubclass::GetEntryForIndexImpl(
*holder, *backing_store, index) != kMaxUInt32; *holder, *backing_store, index, filter) != kMaxUInt32;
} }
virtual Handle<Object> Get(Handle<FixedArrayBase> backing_store, virtual Handle<Object> Get(Handle<FixedArrayBase> backing_store,
...@@ -868,25 +871,51 @@ class ElementsAccessorBase : public ElementsAccessor { ...@@ -868,25 +871,51 @@ class ElementsAccessorBase : public ElementsAccessor {
from, from_start, *to, from_kind, to_start, packed_size, copy_size); from, from_start, *to, from_kind, to_start, packed_size, copy_size);
} }
static void CollectElementIndicesImpl(Handle<JSObject> object,
Handle<FixedArrayBase> backing_store,
KeyAccumulator* keys, uint32_t range,
PropertyAttributes filter,
uint32_t offset) {
uint32_t length = 0;
if (object->IsJSArray()) {
length = Smi::cast(JSArray::cast(*object)->length())->value();
} else {
length =
ElementsAccessorSubclass::GetCapacityImpl(*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;
keys->AddKey(i);
}
}
virtual void CollectElementIndices(Handle<JSObject> object,
Handle<FixedArrayBase> backing_store,
KeyAccumulator* keys, uint32_t range,
PropertyAttributes filter,
uint32_t offset) final {
ElementsAccessorSubclass::CollectElementIndicesImpl(
object, backing_store, keys, range, filter, offset);
};
virtual void AddElementsToKeyAccumulator(Handle<JSObject> receiver, virtual void AddElementsToKeyAccumulator(Handle<JSObject> receiver,
KeyAccumulator* accumulator, KeyAccumulator* accumulator,
KeyFilter filter) final { AddKeyConversion convert) final {
Handle<FixedArrayBase> from(receiver->elements()); Handle<FixedArrayBase> from(receiver->elements());
uint32_t add_length = uint32_t add_length =
ElementsAccessorSubclass::GetCapacityImpl(*receiver, *from); ElementsAccessorSubclass::GetCapacityImpl(*receiver, *from);
if (add_length == 0) return; if (add_length == 0) return;
accumulator->PrepareForComparisons(add_length);
int prev_key_count = accumulator->GetLength();
for (uint32_t i = 0; i < add_length; i++) { for (uint32_t i = 0; i < add_length; i++) {
if (!ElementsAccessorSubclass::HasEntryImpl(*from, i)) continue; if (!ElementsAccessorSubclass::HasEntryImpl(*from, i)) continue;
Handle<Object> value = ElementsAccessorSubclass::GetImpl(from, i); Handle<Object> value = ElementsAccessorSubclass::GetImpl(from, i);
DCHECK(!value->IsTheHole()); DCHECK(!value->IsTheHole());
DCHECK(!value->IsAccessorPair()); DCHECK(!value->IsAccessorPair());
DCHECK(!value->IsExecutableAccessorInfo()); DCHECK(!value->IsExecutableAccessorInfo());
if (filter == SKIP_SYMBOLS && value->IsSymbol()) { accumulator->AddKey(value, convert);
continue;
}
accumulator->AddKey(value, prev_key_count);
} }
} }
...@@ -895,7 +924,8 @@ class ElementsAccessorBase : public ElementsAccessor { ...@@ -895,7 +924,8 @@ class ElementsAccessorBase : public ElementsAccessor {
return backing_store->length(); return backing_store->length();
} }
uint32_t GetCapacity(JSObject* holder, FixedArrayBase* backing_store) final { virtual uint32_t GetCapacity(JSObject* holder,
FixedArrayBase* backing_store) final {
return ElementsAccessorSubclass::GetCapacityImpl(holder, backing_store); return ElementsAccessorSubclass::GetCapacityImpl(holder, backing_store);
} }
...@@ -910,7 +940,8 @@ class ElementsAccessorBase : public ElementsAccessor { ...@@ -910,7 +940,8 @@ class ElementsAccessorBase : public ElementsAccessor {
static uint32_t GetEntryForIndexImpl(JSObject* holder, static uint32_t GetEntryForIndexImpl(JSObject* holder,
FixedArrayBase* backing_store, FixedArrayBase* backing_store,
uint32_t index) { uint32_t index,
PropertyAttributes filter) {
if (IsHoleyElementsKind(kind())) { if (IsHoleyElementsKind(kind())) {
return index < ElementsAccessorSubclass::GetCapacityImpl(holder, return index < ElementsAccessorSubclass::GetCapacityImpl(holder,
backing_store) && backing_store) &&
...@@ -928,7 +959,7 @@ class ElementsAccessorBase : public ElementsAccessor { ...@@ -928,7 +959,7 @@ class ElementsAccessorBase : public ElementsAccessor {
FixedArrayBase* backing_store, FixedArrayBase* backing_store,
uint32_t index) final { uint32_t index) final {
return ElementsAccessorSubclass::GetEntryForIndexImpl(holder, backing_store, return ElementsAccessorSubclass::GetEntryForIndexImpl(holder, backing_store,
index); index, NONE);
} }
static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
...@@ -1090,19 +1121,50 @@ class DictionaryElementsAccessor ...@@ -1090,19 +1121,50 @@ class DictionaryElementsAccessor
} }
static uint32_t GetEntryForIndexImpl(JSObject* holder, FixedArrayBase* store, static uint32_t GetEntryForIndexImpl(JSObject* holder, FixedArrayBase* store,
uint32_t index) { uint32_t index,
PropertyAttributes filter) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
SeededNumberDictionary* dict = SeededNumberDictionary::cast(store); SeededNumberDictionary* dictionary = SeededNumberDictionary::cast(store);
int entry = dict->FindEntry(index); int entry = dictionary->FindEntry(index);
return entry == SeededNumberDictionary::kNotFound if (entry == SeededNumberDictionary::kNotFound) return kMaxUInt32;
? kMaxUInt32 if (filter != NONE) {
: static_cast<uint32_t>(entry); PropertyDetails details = dictionary->DetailsAt(entry);
PropertyAttributes attr = details.attributes();
if ((attr & filter) != 0) return kMaxUInt32;
}
return static_cast<uint32_t>(entry);
} }
static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store, static PropertyDetails GetDetailsImpl(FixedArrayBase* backing_store,
uint32_t entry) { uint32_t entry) {
return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry); return SeededNumberDictionary::cast(backing_store)->DetailsAt(entry);
} }
static void CollectElementIndicesImpl(Handle<JSObject> object,
Handle<FixedArrayBase> backing_store,
KeyAccumulator* keys, uint32_t range,
PropertyAttributes filter,
uint32_t offset) {
Handle<SeededNumberDictionary> dictionary =
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);
PropertyAttributes attr = details.attributes();
if ((attr & filter) != 0) continue;
keys->AddKey(index);
}
keys->SortCurrentElementsList();
}
}; };
...@@ -1759,7 +1821,8 @@ class TypedElementsAccessor ...@@ -1759,7 +1821,8 @@ class TypedElementsAccessor
static uint32_t GetEntryForIndexImpl(JSObject* holder, static uint32_t GetEntryForIndexImpl(JSObject* holder,
FixedArrayBase* backing_store, FixedArrayBase* backing_store,
uint32_t index) { uint32_t index,
PropertyAttributes filter) {
return index < AccessorClass::GetCapacityImpl(holder, backing_store) return index < AccessorClass::GetCapacityImpl(holder, backing_store)
? index ? index
: kMaxUInt32; : kMaxUInt32;
...@@ -1893,14 +1956,15 @@ class SloppyArgumentsElementsAccessor ...@@ -1893,14 +1956,15 @@ class SloppyArgumentsElementsAccessor
static uint32_t GetEntryForIndexImpl(JSObject* holder, static uint32_t GetEntryForIndexImpl(JSObject* holder,
FixedArrayBase* parameters, FixedArrayBase* parameters,
uint32_t index) { uint32_t index,
PropertyAttributes filter) {
FixedArray* parameter_map = FixedArray::cast(parameters); FixedArray* parameter_map = FixedArray::cast(parameters);
Object* probe = GetParameterMapArg(parameter_map, index); Object* probe = GetParameterMapArg(parameter_map, index);
if (!probe->IsTheHole()) return index; if (!probe->IsTheHole()) return index;
FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
uint32_t entry = uint32_t entry = ArgumentsAccessor::GetEntryForIndexImpl(holder, arguments,
ArgumentsAccessor::GetEntryForIndexImpl(holder, arguments, index); index, filter);
if (entry == kMaxUInt32) return entry; if (entry == kMaxUInt32) return entry;
return (parameter_map->length() - 2) + entry; return (parameter_map->length() - 2) + entry;
} }
......
...@@ -22,6 +22,14 @@ class ElementsAccessor { ...@@ -22,6 +22,14 @@ class ElementsAccessor {
const char* name() const { return name_; } const char* name() const { return name_; }
// Returns a shared ElementsAccessor for the specified ElementsKind.
static ElementsAccessor* ForKind(ElementsKind elements_kind) {
DCHECK(static_cast<int>(elements_kind) < kElementsKindCount);
return elements_accessors_[elements_kind];
}
static ElementsAccessor* ForArray(Handle<FixedArrayBase> array);
// Checks the elements of an object for consistency, asserting when a problem // Checks the elements of an object for consistency, asserting when a problem
// is found. // is found.
virtual void Validate(Handle<JSObject> obj) = 0; virtual void Validate(Handle<JSObject> obj) = 0;
...@@ -30,12 +38,19 @@ class ElementsAccessor { ...@@ -30,12 +38,19 @@ class ElementsAccessor {
// without iterating up the prototype chain. The caller can optionally pass // without iterating up the prototype chain. The caller can optionally pass
// in the backing store to use for the check, which must be compatible with // in the backing store to use for the check, which must be compatible with
// the ElementsKind of the ElementsAccessor. If backing_store is NULL, the // the ElementsKind of the ElementsAccessor. If backing_store is NULL, the
// holder->elements() is used as the backing store. // holder->elements() is used as the backing store. If a |filter| is
// specified the PropertyAttributes of the element at the given index
// are compared to the given |filter|. If they match/overlap the given
// index is ignored. Note that only Dictionary elements have custom
// PropertyAttributes associated, hence the |filter| argument is ignored for
// all but DICTIONARY_ELEMENTS and SLOW_SLOPPY_ARGUMENTS_ELEMENTS.
virtual bool HasElement(Handle<JSObject> holder, uint32_t index, virtual bool HasElement(Handle<JSObject> holder, uint32_t index,
Handle<FixedArrayBase> backing_store) = 0; Handle<FixedArrayBase> backing_store,
PropertyAttributes filter = NONE) = 0;
inline bool HasElement(Handle<JSObject> holder, uint32_t index) { inline bool HasElement(Handle<JSObject> holder, uint32_t index,
return HasElement(holder, index, handle(holder->elements())); PropertyAttributes filter = NONE) {
return HasElement(holder, index, handle(holder->elements()), filter);
} }
// Returns true if the backing store is compact in the given range // Returns true if the backing store is compact in the given range
...@@ -97,20 +112,31 @@ class ElementsAccessor { ...@@ -97,20 +112,31 @@ class ElementsAccessor {
*from_holder, 0, from_kind, to, 0, kCopyToEndAndInitializeToHole); *from_holder, 0, from_kind, to, 0, kCopyToEndAndInitializeToHole);
} }
virtual void GrowCapacityAndConvert(Handle<JSObject> object, // Copy all indices that have elements from |object| into the given
uint32_t capacity) = 0; // KeyAccumulator. For Dictionary-based element-kinds we filter out elements
// whose PropertyAttribute match |filter|.
virtual void CollectElementIndices(Handle<JSObject> object,
Handle<FixedArrayBase> backing_store,
KeyAccumulator* keys,
uint32_t range = kMaxUInt32,
PropertyAttributes filter = NONE,
uint32_t offset = 0) = 0;
inline void CollectElementIndices(Handle<JSObject> object,
KeyAccumulator* keys,
uint32_t range = kMaxUInt32,
PropertyAttributes filter = NONE,
uint32_t offset = 0) {
CollectElementIndices(object, handle(object->elements()), keys, range,
filter, offset);
}
virtual void AddElementsToKeyAccumulator(Handle<JSObject> receiver, virtual void AddElementsToKeyAccumulator(Handle<JSObject> receiver,
KeyAccumulator* accumulator, KeyAccumulator* accumulator,
KeyFilter filter) = 0; AddKeyConversion convert) = 0;
// Returns a shared ElementsAccessor for the specified ElementsKind. virtual void GrowCapacityAndConvert(Handle<JSObject> object,
static ElementsAccessor* ForKind(ElementsKind elements_kind) { uint32_t capacity) = 0;
DCHECK(static_cast<int>(elements_kind) < kElementsKindCount);
return elements_accessors_[elements_kind];
}
static ElementsAccessor* ForArray(Handle<FixedArrayBase> array);
static void InitializeOncePerProcess(); static void InitializeOncePerProcess();
static void TearDown(); static void TearDown();
...@@ -158,8 +184,6 @@ class ElementsAccessor { ...@@ -158,8 +184,6 @@ class ElementsAccessor {
static ElementsAccessor* ForArray(FixedArrayBase* array); static ElementsAccessor* ForArray(FixedArrayBase* array);
virtual uint32_t GetCapacity(JSObject* holder,
FixedArrayBase* backing_store) = 0;
// Element handlers distinguish between entries and indices when they // Element handlers distinguish between entries and indices when they
// manipulate elements. Entries refer to elements in terms of their location // manipulate elements. Entries refer to elements in terms of their location
...@@ -176,6 +200,8 @@ class ElementsAccessor { ...@@ -176,6 +200,8 @@ class ElementsAccessor {
uint32_t entry) = 0; uint32_t entry) = 0;
private: private:
virtual uint32_t GetCapacity(JSObject* holder,
FixedArrayBase* backing_store) = 0;
static ElementsAccessor** elements_accessors_; static ElementsAccessor** elements_accessors_;
const char* name_; const char* name_;
......
...@@ -286,6 +286,24 @@ bool Object::KeyEquals(Object* second) { ...@@ -286,6 +286,24 @@ bool Object::KeyEquals(Object* second) {
} }
bool Object::FilterKey(PropertyAttributes filter) {
if ((filter & SYMBOLIC) && IsSymbol()) {
return true;
}
if ((filter & PRIVATE_SYMBOL) && IsSymbol() &&
Symbol::cast(this)->is_private()) {
return true;
}
if ((filter & STRING) && !IsSymbol()) {
return true;
}
return false;
}
Handle<Object> Object::NewStorageFor(Isolate* isolate, Handle<Object> Object::NewStorageFor(Isolate* isolate,
Handle<Object> object, Handle<Object> object,
Representation representation) { Representation representation) {
......
...@@ -24,13 +24,14 @@ ...@@ -24,13 +24,14 @@
#include "src/deoptimizer.h" #include "src/deoptimizer.h"
#include "src/elements.h" #include "src/elements.h"
#include "src/execution.h" #include "src/execution.h"
#include "src/field-index-inl.h"
#include "src/field-index.h" #include "src/field-index.h"
#include "src/field-index-inl.h"
#include "src/full-codegen/full-codegen.h" #include "src/full-codegen/full-codegen.h"
#include "src/hydrogen.h" #include "src/hydrogen.h"
#include "src/ic/ic.h" #include "src/ic/ic.h"
#include "src/interpreter/bytecodes.h" #include "src/interpreter/bytecodes.h"
#include "src/isolate-inl.h" #include "src/isolate-inl.h"
#include "src/list.h"
#include "src/log.h" #include "src/log.h"
#include "src/lookup.h" #include "src/lookup.h"
#include "src/macro-assembler.h" #include "src/macro-assembler.h"
...@@ -7329,24 +7330,6 @@ bool JSReceiver::IsSimpleEnum() { ...@@ -7329,24 +7330,6 @@ bool JSReceiver::IsSimpleEnum() {
} }
static bool FilterKey(Object* key, PropertyAttributes filter) {
if ((filter & SYMBOLIC) && key->IsSymbol()) {
return true;
}
if ((filter & PRIVATE_SYMBOL) &&
key->IsSymbol() && Symbol::cast(key)->is_private()) {
return true;
}
if ((filter & STRING) && !key->IsSymbol()) {
return true;
}
return false;
}
int Map::NumberOfDescribedProperties(DescriptorFlag which, int Map::NumberOfDescribedProperties(DescriptorFlag which,
PropertyAttributes filter) { PropertyAttributes filter) {
int result = 0; int result = 0;
...@@ -7356,7 +7339,7 @@ int Map::NumberOfDescribedProperties(DescriptorFlag which, ...@@ -7356,7 +7339,7 @@ int Map::NumberOfDescribedProperties(DescriptorFlag which,
: NumberOfOwnDescriptors(); : NumberOfOwnDescriptors();
for (int i = 0; i < limit; i++) { for (int i = 0; i < limit; i++) {
if ((descs->GetDetails(i).attributes() & filter) == 0 && if ((descs->GetDetails(i).attributes() & filter) == 0 &&
!FilterKey(descs->GetKey(i), filter)) { !descs->GetKey(i)->FilterKey(filter)) {
result++; result++;
} }
} }
...@@ -7507,124 +7490,225 @@ Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object, ...@@ -7507,124 +7490,225 @@ Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object,
} }
Handle<FixedArray> KeyAccumulator::GetKeys() { KeyAccumulator::~KeyAccumulator() {
for (int i = 0; i < elements_.length(); i++) {
delete elements_[i];
}
}
Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
if (length_ == 0) { if (length_ == 0) {
return isolate_->factory()->empty_fixed_array(); return isolate_->factory()->empty_fixed_array();
} }
if (set_.is_null()) { // Make sure we have all the lengths collected.
keys_->Shrink(length_); NextPrototype();
return keys_;
} // Assemble the result array by first adding the element keys and then
// copy over results from set_ // the property keys. We use the total number of keys per level in
// |protoLengths_| and the available element keys in the corresponding bucket
// in |elements_| to deduce the number of keys to take from the |properties_|
// set.
Handle<FixedArray> result = isolate_->factory()->NewFixedArray(length_); Handle<FixedArray> result = isolate_->factory()->NewFixedArray(length_);
for (int i = 0; i < length_; i++) { int index = 0;
result->set(i, set_->KeyAt(i)); int properties_index = 0;
for (int level = 0; level < levelLengths_.length(); level++) {
int num_total = levelLengths_[level];
int num_elements = 0;
if (num_total < 0) {
// If the total is negative, the current level contains properties from a
// proxy, hence we skip the integer keys in |elements_| since proxies
// define the complete ordering.
num_total = -num_total;
} else if (level < elements_.length()) {
List<uint32_t>* elements = elements_[level];
num_elements = elements->length();
for (int i = 0; i < num_elements; i++) {
Handle<Object> key;
if (convert == KEEP_NUMBERS) {
key = isolate_->factory()->NewNumberFromUint(elements->at(i));
} else {
key = isolate_->factory()->Uint32ToString(elements->at(i));
}
result->set(index, *key);
index++;
}
}
// Add the property keys for this prototype level.
int num_properties = num_total - num_elements;
for (int i = 0; i < num_properties; i++) {
Object* key = properties_->KeyAt(properties_index);
result->set(index, key);
index++;
properties_index++;
} }
}
DCHECK_EQ(index, length_);
return result; return result;
} }
void KeyAccumulator::AddKey(Handle<Object> key, int check_limit) { namespace {
#ifdef ENABLE_SLOW_DCHECKS
if (FLAG_enable_slow_asserts) { class FindKey {
DCHECK(key->IsNumber() || key->IsName()); public:
explicit FindKey(uint32_t key) : key_(key) {}
int operator()(uint32_t* entry) {
if (*entry == key_) return 0;
return *entry < key_ ? -1 : 1;
} }
#endif
if (!set_.is_null()) { private:
set_ = OrderedHashSet::Add(set_, key); uint32_t key_;
length_ = set_->NumberOfElements(); };
return;
bool AccumulatorHasKey(List<uint32_t>* sub_elements, uint32_t key) {
int index = SortedListBSearch(*sub_elements, FindKey(key));
return index != -1;
}
} // namespace
bool KeyAccumulator::AddKey(uint32_t key) {
// Make sure we do not add keys to a proxy-level (see AddKeysFromProxy).
DCHECK_LE(0, levelLength_);
int lookup_limit = elements_.length();
for (int i = 0; i < lookup_limit; i++) {
if (AccumulatorHasKey(elements_[i], key)) return false;
} }
// check if we already have the key in the case we are still using elements_[lookup_limit - 1]->Add(key);
// the keys_ FixedArray length_++;
check_limit = Min(check_limit, length_); levelLength_++;
for (int i = 0; i < check_limit; i++) { return true;
Object* current = keys_->get(i); }
if (current->KeyEquals(*key)) return;
bool KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) {
return AddKey(handle(key, isolate_), convert);
}
bool KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
if (filter_ == SKIP_SYMBOLS && key->IsSymbol()) {
return false;
}
// Make sure we do not add keys to a proxy-level (see AddKeysFromProxy).
DCHECK_LE(0, levelLength_);
// In some cases (e.g. proxies) we might get in String-converted ints which
// should be added to the elements list instead of the properties. For
// proxies we have to convert as well but also respect the original order.
// Therefore we add a converted key to both sides
if (convert == CONVERT_TO_ARRAY_INDEX || convert == PROXY_MAGIC) {
uint32_t index = 0;
int prev_length = length_;
int prev_proto = levelLength_;
bool was_array_index = false;
bool key_was_added = false;
if ((key->IsString() && Handle<String>::cast(key)->AsArrayIndex(&index)) ||
key->ToArrayIndex(&index)) {
key_was_added = AddKey(index);
was_array_index = true;
if (convert == CONVERT_TO_ARRAY_INDEX) return key_was_added;
}
if (was_array_index && convert == PROXY_MAGIC) {
// If we had an array index (number) and it wasn't added, the key
// already existed before, hence we cannot add it to the properties
// keys as it would lead to duplicate entries.
if (!key_was_added) {
return false;
}
length_ = prev_length;
levelLength_ = prev_proto;
}
}
if (properties_.is_null()) {
properties_ = OrderedHashSet::Allocate(isolate_, 16);
}
// TODO(cbruni): remove this conversion once we throw the correct TypeError
// for non-string/symbol elements returned by proxies
if (convert == PROXY_MAGIC && key->IsNumber()) {
key = isolate_->factory()->NumberToString(key);
} }
EnsureCapacity(length_); int prev_size = properties_->NumberOfElements();
keys_->set(length_, *key); properties_ = OrderedHashSet::Add(properties_, key);
if (prev_size < properties_->NumberOfElements()) {
length_++; length_++;
levelLength_++;
}
return true;
} }
void KeyAccumulator::AddKeys(Handle<FixedArray> array, KeyFilter filter) { void KeyAccumulator::AddKeys(Handle<FixedArray> array,
AddKeyConversion convert) {
int add_length = array->length(); int add_length = array->length();
if (add_length == 0) return; if (add_length == 0) return;
if (keys_.is_null() && filter == INCLUDE_SYMBOLS) {
keys_ = array;
length_ = keys_->length();
return;
}
PrepareForComparisons(add_length);
int previous_key_count = length_;
for (int i = 0; i < add_length; i++) { for (int i = 0; i < add_length; i++) {
Handle<Object> current(array->get(i), isolate_); Handle<Object> current(array->get(i), isolate_);
if (filter == SKIP_SYMBOLS && current->IsSymbol()) continue; AddKey(current);
AddKey(current, previous_key_count);
} }
} }
void KeyAccumulator::AddKeys(Handle<JSObject> array_like, KeyFilter filter) { void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
AddKeyConversion convert) {
DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements()); DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements());
ElementsAccessor* accessor = array_like->GetElementsAccessor(); ElementsAccessor* accessor = array_like->GetElementsAccessor();
accessor->AddElementsToKeyAccumulator(array_like, this, filter); accessor->AddElementsToKeyAccumulator(array_like, this, convert);
} }
void KeyAccumulator::PrepareForComparisons(int count) { void KeyAccumulator::AddKeysFromProxy(Handle<JSObject> array_like) {
// Depending on how many comparisons we do we should switch to the // Proxies define a complete list of keys with no distinction of
// hash-table-based checks which have a one-time overhead for // elements and properties, which breaks the normal assumption for the
// initializing but O(1) for HasKey checks. // KeyAccumulator.
if (!set_.is_null()) return; AddKeys(array_like, PROXY_MAGIC);
// These limits were obtained through evaluation of several microbenchmarks. // Invert the current length to indicate a present proxy, so we can ignore
if (length_ * count < 100) return; // element keys for this level. Otherwise we would not fully respect the order
// Don't use a set for few elements // given by the proxy.
if (length_ < 100 && count < 20) return; levelLength_ = -levelLength_;
set_ = OrderedHashSet::Allocate(isolate_, length_);
for (int i = 0; i < length_; i++) {
Handle<Object> value(keys_->get(i), isolate_);
set_ = OrderedHashSet::Add(set_, value);
}
} }
void KeyAccumulator::EnsureCapacity(int capacity) { namespace {
if (keys_.is_null() || keys_->length() <= capacity) {
Grow(); // Used for sorting indices in a List<uint32_t>.
} int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
uint32_t a = *ap;
uint32_t b = *bp;
return (a == b) ? 0 : (a < b) ? -1 : 1;
} }
void KeyAccumulator::Grow() { } // namespace
// The OrderedHashSet handles growing by itself.
if (!set_.is_null()) return;
// Otherwise, grow the internal keys_ FixedArray void KeyAccumulator::SortCurrentElementsList() {
int capacity = keys_.is_null() ? 16 : keys_->length() * 2 + 16; if (elements_.length() == 0) return;
Handle<FixedArray> new_keys = isolate_->factory()->NewFixedArray(capacity); List<uint32_t>* element_keys = elements_[elements_.length() - 1];
if (keys_.is_null()) { element_keys->Sort(&compareUInt32);
keys_ = new_keys; }
return;
}
int buffer_length = keys_->length(); void KeyAccumulator::NextPrototype() {
{ // Store the protoLength on the first call of this method.
DisallowHeapAllocation no_gc; if (!elements_.is_empty()) {
WriteBarrierMode mode = new_keys->GetWriteBarrierMode(no_gc); levelLengths_.Add(levelLength_);
for (int i = 0; i < buffer_length; i++) {
new_keys->set(i, keys_->get(i), mode);
}
} }
keys_ = new_keys; elements_.Add(new List<uint32_t>());
levelLength_ = 0;
} }
MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
KeyCollectionType type, KeyCollectionType type,
KeyFilter filter) { KeyFilter filter,
GetKeysConversion getConversion) {
USE(ContainsOnlyValidKeys); USE(ContainsOnlyValidKeys);
Isolate* isolate = object->GetIsolate(); Isolate* isolate = object->GetIsolate();
KeyAccumulator accumulator(isolate); KeyAccumulator accumulator(isolate, filter);
Handle<JSFunction> arguments_function( Handle<JSFunction> arguments_function(
JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor())); JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor()));
...@@ -7635,6 +7719,7 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, ...@@ -7635,6 +7719,7 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
for (PrototypeIterator iter(isolate, object, for (PrototypeIterator iter(isolate, object,
PrototypeIterator::START_AT_RECEIVER); PrototypeIterator::START_AT_RECEIVER);
!iter.IsAtEnd(end); iter.Advance()) { !iter.IsAtEnd(end); iter.Advance()) {
accumulator.NextPrototype();
if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) { if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
Handle<JSProxy> proxy = PrototypeIterator::GetCurrent<JSProxy>(iter); Handle<JSProxy> proxy = PrototypeIterator::GetCurrent<JSProxy>(iter);
Handle<Object> args[] = { proxy }; Handle<Object> args[] = { proxy };
...@@ -7647,7 +7732,7 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, ...@@ -7647,7 +7732,7 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
arraysize(args), arraysize(args),
args), args),
FixedArray); FixedArray);
accumulator.AddKeys(Handle<JSObject>::cast(names), filter); accumulator.AddKeysFromProxy(Handle<JSObject>::cast(names));
break; break;
} }
...@@ -7663,21 +7748,16 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, ...@@ -7663,21 +7748,16 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
break; break;
} }
// Compute the element keys. JSObject::CollectOwnElementKeys(current, &accumulator,
Handle<FixedArray> element_keys = static_cast<PropertyAttributes>(DONT_ENUM));
isolate->factory()->NewFixedArray(current->NumberOfEnumElements());
current->GetEnumElementKeys(*element_keys);
accumulator.AddKeys(element_keys, filter);
DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys()));
// Add the element keys from the interceptor. // Add the element keys from the interceptor.
if (current->HasIndexedInterceptor()) { if (current->HasIndexedInterceptor()) {
Handle<JSObject> result; Handle<JSObject> result;
if (JSObject::GetKeysForIndexedInterceptor( if (JSObject::GetKeysForIndexedInterceptor(current, object)
current, object).ToHandle(&result)) { .ToHandle(&result)) {
accumulator.AddKeys(result, filter); accumulator.AddKeys(result, CONVERT_TO_ARRAY_INDEX);
} }
DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys()));
} }
if (filter == SKIP_SYMBOLS) { if (filter == SKIP_SYMBOLS) {
...@@ -7697,33 +7777,27 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, ...@@ -7697,33 +7777,27 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
!current->HasNamedInterceptor() && !current->HasNamedInterceptor() &&
!current->HasIndexedInterceptor()); !current->HasIndexedInterceptor());
// Compute the property keys and cache them if possible. // Compute the property keys and cache them if possible.
Handle<FixedArray> enum_keys = Handle<FixedArray> enum_keys =
JSObject::GetEnumPropertyKeys(current, cache_enum_length); JSObject::GetEnumPropertyKeys(current, cache_enum_length);
accumulator.AddKeys(enum_keys, filter); accumulator.AddKeys(enum_keys);
} else { } else {
DCHECK(filter == INCLUDE_SYMBOLS); DCHECK(filter == INCLUDE_SYMBOLS);
PropertyAttributes attr_filter = PropertyAttributes attr_filter =
static_cast<PropertyAttributes>(DONT_ENUM | PRIVATE_SYMBOL); static_cast<PropertyAttributes>(DONT_ENUM | PRIVATE_SYMBOL);
Handle<FixedArray> property_keys = isolate->factory()->NewFixedArray( JSObject::CollectOwnElementKeys(current, &accumulator, attr_filter);
current->NumberOfOwnProperties(attr_filter));
current->GetOwnPropertyNames(*property_keys, 0, attr_filter);
accumulator.AddKeys(property_keys, filter);
} }
DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys()));
// Add the property keys from the interceptor. // Add the property keys from the interceptor.
if (current->HasNamedInterceptor()) { if (current->HasNamedInterceptor()) {
Handle<JSObject> result; Handle<JSObject> result;
if (JSObject::GetKeysForNamedInterceptor( if (JSObject::GetKeysForNamedInterceptor(current, object)
current, object).ToHandle(&result)) { .ToHandle(&result)) {
accumulator.AddKeys(result, filter); accumulator.AddKeys(result);
} }
DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys()));
} }
} }
Handle<FixedArray> keys = accumulator.GetKeys(); Handle<FixedArray> keys = accumulator.GetKeys(getConversion);
DCHECK(ContainsOnlyValidKeys(keys)); DCHECK(ContainsOnlyValidKeys(keys));
return keys; return keys;
} }
...@@ -14680,7 +14754,7 @@ int JSObject::GetOwnPropertyNames(FixedArray* storage, int index, ...@@ -14680,7 +14754,7 @@ int JSObject::GetOwnPropertyNames(FixedArray* storage, int index,
DescriptorArray* descs = map()->instance_descriptors(); DescriptorArray* descs = map()->instance_descriptors();
for (int i = 0; i < real_size; i++) { for (int i = 0; i < real_size; i++) {
if ((descs->GetDetails(i).attributes() & filter) == 0 && if ((descs->GetDetails(i).attributes() & filter) == 0 &&
!FilterKey(descs->GetKey(i), filter)) { !descs->GetKey(i)->FilterKey(filter)) {
storage->set(index++, descs->GetKey(i)); storage->set(index++, descs->GetKey(i));
} }
} }
...@@ -14715,12 +14789,35 @@ int JSObject::NumberOfEnumElements() { ...@@ -14715,12 +14789,35 @@ int JSObject::NumberOfEnumElements() {
} }
void JSObject::CollectOwnElementKeys(Handle<JSObject> object,
KeyAccumulator* keys,
PropertyAttributes filter) {
uint32_t string_keys = 0;
// If this is a String wrapper, add the string indices first,
// as they're guaranteed to precede the elements in numerical order
// and ascending order is required by ECMA-262, 6th, 9.1.12.
if (object->IsJSValue()) {
Object* val = JSValue::cast(*object)->value();
if (val->IsString()) {
String* str = String::cast(val);
string_keys = str->length();
for (uint32_t i = 0; i < string_keys; i++) {
keys->AddKey(i);
}
}
}
ElementsAccessor* accessor = object->GetElementsAccessor();
accessor->CollectElementIndices(object, keys, kMaxUInt32, filter, 0);
}
int JSObject::GetOwnElementKeys(FixedArray* storage, int JSObject::GetOwnElementKeys(FixedArray* storage,
PropertyAttributes filter) { PropertyAttributes filter) {
int counter = 0; int counter = 0;
// If this is a String wrapper, add the string indices first, // If this is a String wrapper, add the string indices first,
// as they're guaranteed to preced the elements in numerical order // as they're guaranteed to precede the elements in numerical order
// and ascending order is required by ECMA-262, 6th, 9.1.12. // and ascending order is required by ECMA-262, 6th, 9.1.12.
if (IsJSValue()) { if (IsJSValue()) {
Object* val = JSValue::cast(this)->value(); Object* val = JSValue::cast(this)->value();
...@@ -14845,11 +14942,6 @@ int JSObject::GetOwnElementKeys(FixedArray* storage, ...@@ -14845,11 +14942,6 @@ int JSObject::GetOwnElementKeys(FixedArray* storage,
} }
int JSObject::GetEnumElementKeys(FixedArray* storage) {
return GetOwnElementKeys(storage, static_cast<PropertyAttributes>(DONT_ENUM));
}
const char* Symbol::PrivateSymbolToName() const { const char* Symbol::PrivateSymbolToName() const {
Heap* heap = GetIsolate()->heap(); Heap* heap = GetIsolate()->heap();
#define SYMBOL_CHECK_AND_PRINT(name) \ #define SYMBOL_CHECK_AND_PRINT(name) \
...@@ -16373,7 +16465,7 @@ int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes( ...@@ -16373,7 +16465,7 @@ int Dictionary<Derived, Shape, Key>::NumberOfElementsFilterAttributes(
int result = 0; int result = 0;
for (int i = 0; i < capacity; i++) { for (int i = 0; i < capacity; i++) {
Object* k = this->KeyAt(i); Object* k = this->KeyAt(i);
if (this->IsKey(k) && !FilterKey(k, filter)) { if (this->IsKey(k) && !k->FilterKey(filter)) {
if (this->IsDeleted(i)) continue; if (this->IsDeleted(i)) continue;
PropertyDetails details = this->DetailsAt(i); PropertyDetails details = this->DetailsAt(i);
PropertyAttributes attr = details.attributes(); PropertyAttributes attr = details.attributes();
...@@ -16389,7 +16481,7 @@ bool Dictionary<Derived, Shape, Key>::HasComplexElements() { ...@@ -16389,7 +16481,7 @@ bool Dictionary<Derived, Shape, Key>::HasComplexElements() {
int capacity = this->Capacity(); int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) { for (int i = 0; i < capacity; i++) {
Object* k = this->KeyAt(i); Object* k = this->KeyAt(i);
if (this->IsKey(k) && !FilterKey(k, NONE)) { if (this->IsKey(k) && !k->FilterKey(NONE)) {
if (this->IsDeleted(i)) continue; if (this->IsDeleted(i)) continue;
PropertyDetails details = this->DetailsAt(i); PropertyDetails details = this->DetailsAt(i);
if (details.type() == ACCESSOR_CONSTANT) return true; if (details.type() == ACCESSOR_CONSTANT) return true;
...@@ -16448,7 +16540,7 @@ int Dictionary<Derived, Shape, Key>::CopyKeysTo( ...@@ -16448,7 +16540,7 @@ int Dictionary<Derived, Shape, Key>::CopyKeysTo(
int capacity = this->Capacity(); int capacity = this->Capacity();
for (int i = 0; i < capacity; i++) { for (int i = 0; i < capacity; i++) {
Object* k = this->KeyAt(i); Object* k = this->KeyAt(i);
if (this->IsKey(k) && !FilterKey(k, filter)) { if (this->IsKey(k) && !k->FilterKey(filter)) {
if (this->IsDeleted(i)) continue; if (this->IsDeleted(i)) continue;
PropertyDetails details = this->DetailsAt(i); PropertyDetails details = this->DetailsAt(i);
PropertyAttributes attr = details.attributes(); PropertyAttributes attr = details.attributes();
......
...@@ -854,6 +854,7 @@ class FixedArrayBase; ...@@ -854,6 +854,7 @@ class FixedArrayBase;
class FunctionLiteral; class FunctionLiteral;
class GlobalObject; class GlobalObject;
class JSBuiltinsObject; class JSBuiltinsObject;
class KeyAccumulator;
class LayoutDescriptor; class LayoutDescriptor;
class LiteralsArray; class LiteralsArray;
class LookupIterator; class LookupIterator;
...@@ -1089,6 +1090,8 @@ class Object { ...@@ -1089,6 +1090,8 @@ class Object {
// 1 all refer to the same property, so this helper will return true. // 1 all refer to the same property, so this helper will return true.
inline bool KeyEquals(Object* other); inline bool KeyEquals(Object* other);
inline bool FilterKey(PropertyAttributes filter);
Handle<HeapType> OptimalType(Isolate* isolate, Representation representation); Handle<HeapType> OptimalType(Isolate* isolate, Representation representation);
inline static Handle<Object> NewStorageFor(Isolate* isolate, inline static Handle<Object> NewStorageFor(Isolate* isolate,
...@@ -1791,6 +1794,9 @@ enum AccessorComponent { ...@@ -1791,6 +1794,9 @@ enum AccessorComponent {
enum KeyFilter { SKIP_SYMBOLS, INCLUDE_SYMBOLS }; enum KeyFilter { SKIP_SYMBOLS, INCLUDE_SYMBOLS };
enum GetKeysConversion { KEEP_NUMBERS, CONVERT_TO_STRING };
enum ShouldThrow { THROW_ON_ERROR, DONT_THROW }; enum ShouldThrow { THROW_ON_ERROR, DONT_THROW };
...@@ -1903,7 +1909,8 @@ class JSReceiver: public HeapObject { ...@@ -1903,7 +1909,8 @@ class JSReceiver: public HeapObject {
// "for (n in object) { }". // "for (n in object) { }".
MUST_USE_RESULT static MaybeHandle<FixedArray> GetKeys( MUST_USE_RESULT static MaybeHandle<FixedArray> GetKeys(
Handle<JSReceiver> object, KeyCollectionType type, Handle<JSReceiver> object, KeyCollectionType type,
KeyFilter filter = SKIP_SYMBOLS); KeyFilter filter = SKIP_SYMBOLS,
GetKeysConversion getConversion = KEEP_NUMBERS);
private: private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSReceiver); DISALLOW_IMPLICIT_CONSTRUCTORS(JSReceiver);
...@@ -2233,6 +2240,9 @@ class JSObject: public JSReceiver { ...@@ -2233,6 +2240,9 @@ class JSObject: public JSReceiver {
// Returns the number of elements on this object filtering out elements // Returns the number of elements on this object filtering out elements
// with the specified attributes (ignoring interceptors). // with the specified attributes (ignoring interceptors).
int GetOwnElementKeys(FixedArray* storage, PropertyAttributes filter); int GetOwnElementKeys(FixedArray* storage, PropertyAttributes filter);
static void CollectOwnElementKeys(Handle<JSObject> object,
KeyAccumulator* keys,
PropertyAttributes filter);
// Count and fill in the enumerable elements into storage. // Count and fill in the enumerable elements into storage.
// (storage->length() == NumberOfEnumElements()). // (storage->length() == NumberOfEnumElements()).
// If storage is NULL, will count the elements without adding // If storage is NULL, will count the elements without adding
...@@ -10714,26 +10724,62 @@ class BooleanBit : public AllStatic { ...@@ -10714,26 +10724,62 @@ class BooleanBit : public AllStatic {
}; };
enum AddKeyConversion { DO_NOT_CONVERT, CONVERT_TO_ARRAY_INDEX, PROXY_MAGIC };
// This is a helper class for JSReceiver::GetKeys which collects and sorts keys.
// GetKeys needs to sort keys per prototype level, first showing the integer
// indices from elements then the strings from the properties. However, this
// does not apply to proxies which are in full control of how the keys are
// sorted.
//
// For performance reasons the KeyAccumulator internally separates integer
// keys in |elements_| into sorted lists per prototype level. String keys are
// collected in |properties_|, a single OrderedHashSet. To separate the keys per
// level later when assembling the final list, |levelLengths_| keeps track of
// the total number of keys (integers + strings) per level.
//
// Only unique keys are kept by the KeyAccumulator, strings are stored in a
// HashSet for inexpensive lookups. Integer keys are kept in sorted lists which
// are more compact and allow for reasonably fast includes check.
class KeyAccumulator final BASE_EMBEDDED { class KeyAccumulator final BASE_EMBEDDED {
public: public:
explicit KeyAccumulator(Isolate* isolate) : isolate_(isolate), length_(0) {} explicit KeyAccumulator(Isolate* isolate,
KeyFilter filter = KeyFilter::SKIP_SYMBOLS)
void AddKey(Handle<Object> key, int check_limit); : isolate_(isolate), filter_(filter), length_(0), levelLength_(0) {}
void AddKeys(Handle<FixedArray> array, KeyFilter filter); ~KeyAccumulator();
void AddKeys(Handle<JSObject> array, KeyFilter filter);
void PrepareForComparisons(int count); bool AddKey(uint32_t key);
Handle<FixedArray> GetKeys(); bool AddKey(Object* key, AddKeyConversion convert = DO_NOT_CONVERT);
bool AddKey(Handle<Object> key, AddKeyConversion convert = DO_NOT_CONVERT);
void AddKeys(Handle<FixedArray> array,
AddKeyConversion convert = DO_NOT_CONVERT);
void AddKeys(Handle<JSObject> array,
AddKeyConversion convert = DO_NOT_CONVERT);
void AddKeysFromProxy(Handle<JSObject> array);
// Jump to the next level, pushing the current |levelLength_| to
// |levelLengths_| and adding a new list to |elements_|.
void NextPrototype();
// Sort the integer indices in the last list in |elements_|
void SortCurrentElementsList();
Handle<FixedArray> GetKeys(GetKeysConversion convert = KEEP_NUMBERS);
int GetLength() { return length_; }
private: private:
void EnsureCapacity(int capacity);
void Grow();
Isolate* isolate_; Isolate* isolate_;
Handle<FixedArray> keys_; KeyFilter filter_;
Handle<OrderedHashSet> set_; // |elements_| contains the sorted element keys (indices) per level.
List<List<uint32_t>*> elements_;
// |protoLengths_| contains the total number of keys (elements + properties)
// per level. Negative values mark counts for a level with keys from a proxy.
List<int> levelLengths_;
// |properties_| contains the property keys per level in insertion order.
Handle<OrderedHashSet> properties_;
// |length_| keeps track of the total number of all element and property keys.
int length_; int length_;
// |levelLength_| keeps track of the total number of keys
// (elements + properties) in the current level.
int levelLength_;
DISALLOW_COPY_AND_ASSIGN(KeyAccumulator); DISALLOW_COPY_AND_ASSIGN(KeyAccumulator);
}; };
......
...@@ -206,6 +206,8 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) { ...@@ -206,6 +206,8 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
} }
KeyAccumulator accumulator(isolate); KeyAccumulator accumulator(isolate);
// No need to separate protoype levels since we only get numbers/element keys
accumulator.NextPrototype();
for (PrototypeIterator iter(isolate, array, for (PrototypeIterator iter(isolate, array,
PrototypeIterator::START_AT_RECEIVER); PrototypeIterator::START_AT_RECEIVER);
!iter.IsAtEnd(); iter.Advance()) { !iter.IsAtEnd(); iter.Advance()) {
...@@ -217,15 +219,12 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) { ...@@ -217,15 +219,12 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
return *isolate->factory()->NewNumberFromUint(length); return *isolate->factory()->NewNumberFromUint(length);
} }
Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
Handle<FixedArray> current_keys = JSObject::CollectOwnElementKeys(current, &accumulator, NONE);
isolate->factory()->NewFixedArray(current->NumberOfOwnElements(NONE));
current->GetOwnElementKeys(*current_keys, NONE);
accumulator.AddKeys(current_keys, INCLUDE_SYMBOLS);
} }
// Erase any keys >= length. // Erase any keys >= length.
// TODO(adamk): Remove this step when the contract of %GetArrayKeys // TODO(adamk): Remove this step when the contract of %GetArrayKeys
// is changed to let this happen on the JS side. // is changed to let this happen on the JS side.
Handle<FixedArray> keys = accumulator.GetKeys(); Handle<FixedArray> keys = accumulator.GetKeys(KEEP_NUMBERS);
for (int i = 0; i < keys->length(); i++) { for (int i = 0; i < keys->length(); i++) {
if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i); if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i);
} }
......
...@@ -981,34 +981,9 @@ RUNTIME_FUNCTION(Runtime_OwnKeys) { ...@@ -981,34 +981,9 @@ RUNTIME_FUNCTION(Runtime_OwnKeys) {
Handle<FixedArray> contents; Handle<FixedArray> contents;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, contents, JSReceiver::GetKeys(object, JSReceiver::OWN_ONLY)); isolate, contents, JSReceiver::GetKeys(object, JSReceiver::OWN_ONLY,
SKIP_SYMBOLS, CONVERT_TO_STRING));
// Some fast paths through GetKeysInFixedArrayFor reuse a cached return *isolate->factory()->NewJSArrayWithElements(contents);
// property array and since the result is mutable we have to create
// a fresh clone on each invocation.
int length = contents->length();
Handle<FixedArray> copy = isolate->factory()->NewFixedArray(length);
int offset = 0;
// Use an outer loop to avoid creating too many handles in the current
// handle scope.
while (offset < length) {
HandleScope scope(isolate);
offset += 100;
int end = Min(offset, length);
for (int i = offset - 100; i < end; i++) {
Object* entry = contents->get(i);
if (entry->IsString()) {
copy->set(i, entry);
} else {
DCHECK(entry->IsNumber());
Handle<Object> entry_handle(entry, isolate);
Handle<Object> entry_str =
isolate->factory()->NumberToString(entry_handle);
copy->set(i, *entry_str);
}
}
}
return *isolate->factory()->NewJSArrayWithElements(copy);
} }
......
...@@ -42,6 +42,7 @@ assertEquals(Object.keys({a:null, b:null}), ['a', 'b']); ...@@ -42,6 +42,7 @@ assertEquals(Object.keys({a:null, b:null}), ['a', 'b']);
assertEquals(Object.keys({b:null, a:null}), ['b', 'a']); assertEquals(Object.keys({b:null, a:null}), ['b', 'a']);
assertEquals(Object.keys([]), []); assertEquals(Object.keys([]), []);
assertEquals(Object.keys([null]), ['0']); assertEquals(Object.keys([null]), ['0']);
assertEquals(Object.keys([undefined]), ['0']);
assertEquals(Object.keys([null,null]), ['0', '1']); assertEquals(Object.keys([null,null]), ['0', '1']);
assertEquals(Object.keys([null,null,,,,null]), ['0', '1', '5']); assertEquals(Object.keys([null,null,,,,null]), ['0', '1', '5']);
assertEquals(Object.keys({__proto__:{a:null}}), []); assertEquals(Object.keys({__proto__:{a:null}}), []);
...@@ -66,3 +67,34 @@ keysBefore[0] = 'x'; ...@@ -66,3 +67,34 @@ keysBefore[0] = 'x';
var keysAfter = Object.keys(literal); var keysAfter = Object.keys(literal);
assertEquals(['a', 'b', 'c'], keysAfter); assertEquals(['a', 'b', 'c'], keysAfter);
assertEquals(['x', 'b', 'c'], keysBefore); assertEquals(['x', 'b', 'c'], keysBefore);
var o = [1, 2, 3];
assertEquals(['0', '1', '2'], Object.keys(o));
Object.defineProperty(o, '0', {
enumerable: false,
});
assertEquals(['1', '2'], Object.keys(o));
(function(){
assertEquals(['0', '1', '2'], Object.keys(arguments));
Object.defineProperty(arguments, '0', {
enumerable: false,
});
assertEquals(['1', '2'], Object.keys(arguments));
})(0,1,2);
(function(a, b){
assertEquals(['0', '1', '2'], Object.keys(arguments));
Object.defineProperty(arguments, '0', {
enumerable: false,
});
assertEquals(['1', '2'], Object.keys(arguments));
})(0,1,2);
var b = [];
assertEquals(0, Object.keys(b).length);
b[0] = undefined;
assertEquals(1, Object.keys(b).length);
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