| Index: src/keys.cc
 | 
| diff --git a/src/key-accumulator.cc b/src/keys.cc
 | 
| similarity index 72%
 | 
| rename from src/key-accumulator.cc
 | 
| rename to src/keys.cc
 | 
| index c2c49969229de3f603ea8494b4543696fef10855..dc696cf10e578ab35b11c4038ea5c274834c2294 100644
 | 
| --- a/src/key-accumulator.cc
 | 
| +++ b/src/keys.cc
 | 
| @@ -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
 | 
| 
 |