Home | History | Annotate | Download | only in src
      1 // Copyright 2013 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/keys.h"
      6 
      7 #include "src/api-arguments.h"
      8 #include "src/elements.h"
      9 #include "src/factory.h"
     10 #include "src/identity-map.h"
     11 #include "src/isolate-inl.h"
     12 #include "src/objects-inl.h"
     13 #include "src/property-descriptor.h"
     14 #include "src/prototype.h"
     15 
     16 namespace v8 {
     17 namespace internal {
     18 
     19 KeyAccumulator::~KeyAccumulator() {
     20 }
     21 
     22 namespace {
     23 
     24 static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
     25   int len = array->length();
     26   for (int i = 0; i < len; i++) {
     27     Object* e = array->get(i);
     28     if (!(e->IsName() || e->IsNumber())) return false;
     29   }
     30   return true;
     31 }
     32 
     33 }  // namespace
     34 
     35 // static
     36 MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
     37     Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
     38     GetKeysConversion keys_conversion, bool is_for_in) {
     39   Isolate* isolate = object->GetIsolate();
     40   FastKeyAccumulator accumulator(isolate, object, mode, filter);
     41   accumulator.set_is_for_in(is_for_in);
     42   return accumulator.GetKeys(keys_conversion);
     43 }
     44 
     45 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
     46   if (keys_.is_null()) {
     47     return isolate_->factory()->empty_fixed_array();
     48   }
     49   if (mode_ == KeyCollectionMode::kOwnOnly &&
     50       keys_->map() == isolate_->heap()->fixed_array_map()) {
     51     return Handle<FixedArray>::cast(keys_);
     52   }
     53   USE(ContainsOnlyValidKeys);
     54   Handle<FixedArray> result =
     55       OrderedHashSet::ConvertToKeysArray(keys(), convert);
     56   DCHECK(ContainsOnlyValidKeys(result));
     57   return result;
     58 }
     59 
     60 void KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) {
     61   AddKey(handle(key, isolate_), convert);
     62 }
     63 
     64 void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
     65   if (key->IsSymbol()) {
     66     if (filter_ & SKIP_SYMBOLS) return;
     67     if (Handle<Symbol>::cast(key)->is_private()) return;
     68   } else if (filter_ & SKIP_STRINGS) {
     69     return;
     70   }
     71   if (IsShadowed(key)) return;
     72   if (keys_.is_null()) {
     73     keys_ = OrderedHashSet::Allocate(isolate_, 16);
     74   }
     75   uint32_t index;
     76   if (convert == CONVERT_TO_ARRAY_INDEX && key->IsString() &&
     77       Handle<String>::cast(key)->AsArrayIndex(&index)) {
     78     key = isolate_->factory()->NewNumberFromUint(index);
     79   }
     80   keys_ = OrderedHashSet::Add(keys(), key);
     81 }
     82 
     83 void KeyAccumulator::AddKeys(Handle<FixedArray> array,
     84                              AddKeyConversion convert) {
     85   int add_length = array->length();
     86   for (int i = 0; i < add_length; i++) {
     87     Handle<Object> current(array->get(i), isolate_);
     88     AddKey(current, convert);
     89   }
     90 }
     91 
     92 void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
     93                              AddKeyConversion convert) {
     94   DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements());
     95   ElementsAccessor* accessor = array_like->GetElementsAccessor();
     96   accessor->AddElementsToKeyAccumulator(array_like, this, convert);
     97 }
     98 
     99 MaybeHandle<FixedArray> FilterProxyKeys(KeyAccumulator* accumulator,
    100                                         Handle<JSProxy> owner,
    101                                         Handle<FixedArray> keys,
    102                                         PropertyFilter filter) {
    103   if (filter == ALL_PROPERTIES) {
    104     // Nothing to do.
    105     return keys;
    106   }
    107   Isolate* isolate = accumulator->isolate();
    108   int store_position = 0;
    109   for (int i = 0; i < keys->length(); ++i) {
    110     Handle<Name> key(Name::cast(keys->get(i)), isolate);
    111     if (key->FilterKey(filter)) continue;  // Skip this key.
    112     if (filter & ONLY_ENUMERABLE) {
    113       PropertyDescriptor desc;
    114       Maybe<bool> found =
    115           JSProxy::GetOwnPropertyDescriptor(isolate, owner, key, &desc);
    116       MAYBE_RETURN(found, MaybeHandle<FixedArray>());
    117       if (!found.FromJust()) continue;
    118       if (!desc.enumerable()) {
    119         accumulator->AddShadowingKey(key);
    120         continue;
    121       }
    122     }
    123     // Keep this key.
    124     if (store_position != i) {
    125       keys->set(store_position, *key);
    126     }
    127     store_position++;
    128   }
    129   if (store_position == 0) return isolate->factory()->empty_fixed_array();
    130   keys->Shrink(store_position);
    131   return keys;
    132 }
    133 
    134 // Returns "nothing" in case of exception, "true" on success.
    135 Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy,
    136                                                Handle<FixedArray> keys) {
    137   // Postpone the enumerable check for for-in to the ForInFilter step.
    138   if (!is_for_in_) {
    139     ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    140         isolate_, keys, FilterProxyKeys(this, proxy, keys, filter_),
    141         Nothing<bool>());
    142     if (mode_ == KeyCollectionMode::kOwnOnly) {
    143       // If we collect only the keys from a JSProxy do not sort or deduplicate.
    144       keys_ = keys;
    145       return Just(true);
    146     }
    147   }
    148   AddKeys(keys, is_for_in_ ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
    149   return Just(true);
    150 }
    151 
    152 Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
    153                                         Handle<JSReceiver> object) {
    154   // Proxies have no hidden prototype and we should not trigger the
    155   // [[GetPrototypeOf]] trap on the last iteration when using
    156   // AdvanceFollowingProxies.
    157   if (mode_ == KeyCollectionMode::kOwnOnly && object->IsJSProxy()) {
    158     MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)),
    159                  Nothing<bool>());
    160     return Just(true);
    161   }
    162 
    163   PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly
    164                                           ? PrototypeIterator::END_AT_NON_HIDDEN
    165                                           : PrototypeIterator::END_AT_NULL;
    166   for (PrototypeIterator iter(isolate_, object, kStartAtReceiver, end);
    167        !iter.IsAtEnd();) {
    168     // Start the shadow checks only after the first prototype has added
    169     // shadowing keys.
    170     if (HasShadowingKeys()) skip_shadow_check_ = false;
    171     Handle<JSReceiver> current =
    172         PrototypeIterator::GetCurrent<JSReceiver>(iter);
    173     Maybe<bool> result = Just(false);  // Dummy initialization.
    174     if (current->IsJSProxy()) {
    175       result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current));
    176     } else {
    177       DCHECK(current->IsJSObject());
    178       result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current));
    179     }
    180     MAYBE_RETURN(result, Nothing<bool>());
    181     if (!result.FromJust()) break;  // |false| means "stop iterating".
    182     // Iterate through proxies but ignore access checks for the ALL_CAN_READ
    183     // case on API objects for OWN_ONLY keys handled in CollectOwnKeys.
    184     if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
    185       return Nothing<bool>();
    186     }
    187     if (!last_non_empty_prototype_.is_null() &&
    188         *last_non_empty_prototype_ == *current) {
    189       break;
    190     }
    191   }
    192   return Just(true);
    193 }
    194 
    195 bool KeyAccumulator::HasShadowingKeys() { return !shadowing_keys_.is_null(); }
    196 
    197 bool KeyAccumulator::IsShadowed(Handle<Object> key) {
    198   if (!HasShadowingKeys() || skip_shadow_check_) return false;
    199   return shadowing_keys_->Has(isolate_, key);
    200 }
    201 
    202 void KeyAccumulator::AddShadowingKey(Object* key) {
    203   if (mode_ == KeyCollectionMode::kOwnOnly) return;
    204   AddShadowingKey(handle(key, isolate_));
    205 }
    206 void KeyAccumulator::AddShadowingKey(Handle<Object> key) {
    207   if (mode_ == KeyCollectionMode::kOwnOnly) return;
    208   if (shadowing_keys_.is_null()) {
    209     shadowing_keys_ = ObjectHashSet::New(isolate_, 16);
    210   }
    211   shadowing_keys_ = ObjectHashSet::Add(shadowing_keys_, key);
    212 }
    213 
    214 namespace {
    215 
    216 void TrySettingEmptyEnumCache(JSReceiver* object) {
    217   Map* map = object->map();
    218   DCHECK_EQ(kInvalidEnumCacheSentinel, map->EnumLength());
    219   if (!map->OnlyHasSimpleProperties()) return;
    220   if (map->IsJSProxyMap()) return;
    221   if (map->NumberOfOwnDescriptors() > 0) {
    222     int number_of_enumerable_own_properties =
    223         map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
    224     if (number_of_enumerable_own_properties > 0) return;
    225   }
    226   DCHECK(object->IsJSObject());
    227   map->SetEnumLength(0);
    228 }
    229 
    230 bool CheckAndInitalizeEmptyEnumCache(JSReceiver* object) {
    231   if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
    232     TrySettingEmptyEnumCache(object);
    233   }
    234   if (object->map()->EnumLength() != 0) return false;
    235   DCHECK(object->IsJSObject());
    236   return !JSObject::cast(object)->HasEnumerableElements();
    237 }
    238 }  // namespace
    239 
    240 void FastKeyAccumulator::Prepare() {
    241   DisallowHeapAllocation no_gc;
    242   // Directly go for the fast path for OWN_ONLY keys.
    243   if (mode_ == KeyCollectionMode::kOwnOnly) return;
    244   // Fully walk the prototype chain and find the last prototype with keys.
    245   is_receiver_simple_enum_ = false;
    246   has_empty_prototype_ = true;
    247   JSReceiver* last_prototype = nullptr;
    248   for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
    249        iter.Advance()) {
    250     JSReceiver* current = iter.GetCurrent<JSReceiver>();
    251     bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
    252     if (has_no_properties) continue;
    253     last_prototype = current;
    254     has_empty_prototype_ = false;
    255   }
    256   if (has_empty_prototype_) {
    257     is_receiver_simple_enum_ =
    258         receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
    259         !JSObject::cast(*receiver_)->HasEnumerableElements();
    260   } else if (last_prototype != nullptr) {
    261     last_non_empty_prototype_ = handle(last_prototype, isolate_);
    262   }
    263 }
    264 
    265 namespace {
    266 static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
    267                                              Handle<FixedArray> array,
    268                                              int length) {
    269   DCHECK_LE(length, array->length());
    270   if (array->length() == length) return array;
    271   return isolate->factory()->CopyFixedArrayUpTo(array, length);
    272 }
    273 
    274 // Initializes and directly returns the enume cache. Users of this function
    275 // have to make sure to never directly leak the enum cache.
    276 Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
    277                                            Handle<JSObject> object) {
    278   Handle<Map> map(object->map());
    279   bool cache_enum_length = map->OnlyHasSimpleProperties();
    280 
    281   Handle<DescriptorArray> descs =
    282       Handle<DescriptorArray>(map->instance_descriptors(), isolate);
    283   int own_property_count = map->EnumLength();
    284   // If the enum length of the given map is set to kInvalidEnumCache, this
    285   // means that the map itself has never used the present enum cache. The
    286   // first step to using the cache is to set the enum length of the map by
    287   // counting the number of own descriptors that are ENUMERABLE_STRINGS.
    288   if (own_property_count == kInvalidEnumCacheSentinel) {
    289     own_property_count =
    290         map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
    291   } else {
    292     DCHECK(
    293         own_property_count ==
    294         map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS));
    295   }
    296 
    297   if (descs->HasEnumCache()) {
    298     Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
    299     // In case the number of properties required in the enum are actually
    300     // present, we can reuse the enum cache. Otherwise, this means that the
    301     // enum cache was generated for a previous (smaller) version of the
    302     // Descriptor Array. In that case we regenerate the enum cache.
    303     if (own_property_count <= keys->length()) {
    304       isolate->counters()->enum_cache_hits()->Increment();
    305       if (cache_enum_length) map->SetEnumLength(own_property_count);
    306       return ReduceFixedArrayTo(isolate, keys, own_property_count);
    307     }
    308   }
    309 
    310   if (descs->IsEmpty()) {
    311     isolate->counters()->enum_cache_hits()->Increment();
    312     if (cache_enum_length) map->SetEnumLength(0);
    313     return isolate->factory()->empty_fixed_array();
    314   }
    315 
    316   isolate->counters()->enum_cache_misses()->Increment();
    317 
    318   Handle<FixedArray> storage =
    319       isolate->factory()->NewFixedArray(own_property_count);
    320   Handle<FixedArray> indices =
    321       isolate->factory()->NewFixedArray(own_property_count);
    322 
    323   int size = map->NumberOfOwnDescriptors();
    324   int index = 0;
    325 
    326   for (int i = 0; i < size; i++) {
    327     PropertyDetails details = descs->GetDetails(i);
    328     if (details.IsDontEnum()) continue;
    329     Object* key = descs->GetKey(i);
    330     if (key->IsSymbol()) continue;
    331     storage->set(index, key);
    332     if (!indices.is_null()) {
    333       if (details.location() == kField) {
    334         DCHECK_EQ(kData, details.kind());
    335         FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
    336         int load_by_field_index = field_index.GetLoadByFieldIndex();
    337         indices->set(index, Smi::FromInt(load_by_field_index));
    338       } else {
    339         indices = Handle<FixedArray>();
    340       }
    341     }
    342     index++;
    343   }
    344   DCHECK(index == storage->length());
    345 
    346   DescriptorArray::SetEnumCache(descs, isolate, storage, indices);
    347   if (cache_enum_length) {
    348     map->SetEnumLength(own_property_count);
    349   }
    350   return storage;
    351 }
    352 
    353 template <bool fast_properties>
    354 MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
    355                                                Handle<JSObject> object,
    356                                                GetKeysConversion convert) {
    357   Handle<FixedArray> keys;
    358   ElementsAccessor* accessor = object->GetElementsAccessor();
    359   if (fast_properties) {
    360     keys = GetFastEnumPropertyKeys(isolate, object);
    361   } else {
    362     // TODO(cbruni): preallocate big enough array to also hold elements.
    363     keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate, object);
    364   }
    365   MaybeHandle<FixedArray> result =
    366       accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
    367 
    368   if (FLAG_trace_for_in_enumerate) {
    369     PrintF("| strings=%d symbols=0 elements=%u || prototypes>=1 ||\n",
    370            keys->length(), result.ToHandleChecked()->length() - keys->length());
    371   }
    372   return result;
    373 }
    374 
    375 bool OnlyHasSimpleProperties(Map* map) {
    376   return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER;
    377 }
    378 
    379 }  // namespace
    380 
    381 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(
    382     GetKeysConversion keys_conversion) {
    383   if (filter_ == ENUMERABLE_STRINGS) {
    384     Handle<FixedArray> keys;
    385     if (GetKeysFast(keys_conversion).ToHandle(&keys)) {
    386       return keys;
    387     }
    388     if (isolate_->has_pending_exception()) return MaybeHandle<FixedArray>();
    389   }
    390 
    391   return GetKeysSlow(keys_conversion);
    392 }
    393 
    394 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
    395     GetKeysConversion keys_conversion) {
    396   bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly;
    397   Map* map = receiver_->map();
    398   if (!own_only || !OnlyHasSimpleProperties(map)) {
    399     return MaybeHandle<FixedArray>();
    400   }
    401 
    402   // From this point on we are certiain to only collect own keys.
    403   DCHECK(receiver_->IsJSObject());
    404   Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
    405 
    406   // Do not try to use the enum-cache for dict-mode objects.
    407   if (map->is_dictionary_map()) {
    408     return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion);
    409   }
    410   int enum_length = receiver_->map()->EnumLength();
    411   if (enum_length == kInvalidEnumCacheSentinel) {
    412     Handle<FixedArray> keys;
    413     // Try initializing the enum cache and return own properties.
    414     if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) {
    415       if (FLAG_trace_for_in_enumerate) {
    416         PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n",
    417                keys->length());
    418       }
    419       is_receiver_simple_enum_ =
    420           object->map()->EnumLength() != kInvalidEnumCacheSentinel;
    421       return keys;
    422     }
    423   }
    424   // The properties-only case failed because there were probably elements on the
    425   // receiver.
    426   return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion);
    427 }
    428 
    429 MaybeHandle<FixedArray>
    430 FastKeyAccumulator::GetOwnKeysWithUninitializedEnumCache() {
    431   Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
    432   // Uninitalized enum cache
    433   Map* map = object->map();
    434   if (object->elements()->length() != 0) {
    435     // Assume that there are elements.
    436     return MaybeHandle<FixedArray>();
    437   }
    438   int number_of_own_descriptors = map->NumberOfOwnDescriptors();
    439   if (number_of_own_descriptors == 0) {
    440     map->SetEnumLength(0);
    441     return isolate_->factory()->empty_fixed_array();
    442   }
    443   // We have no elements but possibly enumerable property keys, hence we can
    444   // directly initialize the enum cache.
    445   Handle<FixedArray> keys = GetFastEnumPropertyKeys(isolate_, object);
    446   if (is_for_in_) return keys;
    447   // Do not leak the enum cache as it might end up as an elements backing store.
    448   return isolate_->factory()->CopyFixedArray(keys);
    449 }
    450 
    451 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
    452     GetKeysConversion keys_conversion) {
    453   KeyAccumulator accumulator(isolate_, mode_, filter_);
    454   accumulator.set_is_for_in(is_for_in_);
    455   accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
    456 
    457   MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
    458                MaybeHandle<FixedArray>());
    459   return accumulator.GetKeys(keys_conversion);
    460 }
    461 
    462 namespace {
    463 
    464 enum IndexedOrNamed { kIndexed, kNamed };
    465 
    466 // Returns |true| on success, |nothing| on exception.
    467 template <class Callback, IndexedOrNamed type>
    468 Maybe<bool> CollectInterceptorKeysInternal(Handle<JSReceiver> receiver,
    469                                            Handle<JSObject> object,
    470                                            Handle<InterceptorInfo> interceptor,
    471                                            KeyAccumulator* accumulator) {
    472   Isolate* isolate = accumulator->isolate();
    473   PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
    474                                  *object, Object::DONT_THROW);
    475   Handle<JSObject> result;
    476   if (!interceptor->enumerator()->IsUndefined(isolate)) {
    477     Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
    478     const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
    479                                            : "interceptor-named-enum";
    480     LOG(isolate, ApiObjectAccess(log_tag, *object));
    481     result = args.Call(enum_fun);
    482   }
    483   RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
    484   if (result.is_null()) return Just(true);
    485   accumulator->AddKeys(
    486       result, type == kIndexed ? CONVERT_TO_ARRAY_INDEX : DO_NOT_CONVERT);
    487   return Just(true);
    488 }
    489 
    490 template <class Callback, IndexedOrNamed type>
    491 Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
    492                                    Handle<JSObject> object,
    493                                    KeyAccumulator* accumulator) {
    494   Isolate* isolate = accumulator->isolate();
    495   if (type == kIndexed) {
    496     if (!object->HasIndexedInterceptor()) return Just(true);
    497   } else {
    498     if (!object->HasNamedInterceptor()) return Just(true);
    499   }
    500   Handle<InterceptorInfo> interceptor(type == kIndexed
    501                                           ? object->GetIndexedInterceptor()
    502                                           : object->GetNamedInterceptor(),
    503                                       isolate);
    504   if ((accumulator->filter() & ONLY_ALL_CAN_READ) &&
    505       !interceptor->all_can_read()) {
    506     return Just(true);
    507   }
    508   return CollectInterceptorKeysInternal<Callback, type>(
    509       receiver, object, interceptor, accumulator);
    510 }
    511 
    512 }  // namespace
    513 
    514 Maybe<bool> KeyAccumulator::CollectOwnElementIndices(
    515     Handle<JSReceiver> receiver, Handle<JSObject> object) {
    516   if (filter_ & SKIP_STRINGS || skip_indices_) return Just(true);
    517 
    518   ElementsAccessor* accessor = object->GetElementsAccessor();
    519   accessor->CollectElementIndices(object, this);
    520 
    521   return CollectInterceptorKeys<v8::IndexedPropertyEnumeratorCallback,
    522                                 kIndexed>(receiver, object, this);
    523 }
    524 
    525 namespace {
    526 
    527 template <bool skip_symbols>
    528 int CollectOwnPropertyNamesInternal(Handle<JSObject> object,
    529                                     KeyAccumulator* keys,
    530                                     Handle<DescriptorArray> descs,
    531                                     int start_index, int limit) {
    532   int first_skipped = -1;
    533   PropertyFilter filter = keys->filter();
    534   KeyCollectionMode mode = keys->mode();
    535   for (int i = start_index; i < limit; i++) {
    536     bool is_shadowing_key = false;
    537     PropertyDetails details = descs->GetDetails(i);
    538 
    539     if ((details.attributes() & filter) != 0) {
    540       if (mode == KeyCollectionMode::kIncludePrototypes) {
    541         is_shadowing_key = true;
    542       } else {
    543         continue;
    544       }
    545     }
    546 
    547     if (filter & ONLY_ALL_CAN_READ) {
    548       if (details.kind() != kAccessor) continue;
    549       Object* accessors = descs->GetValue(i);
    550       if (!accessors->IsAccessorInfo()) continue;
    551       if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
    552     }
    553 
    554     Name* key = descs->GetKey(i);
    555     if (skip_symbols == key->IsSymbol()) {
    556       if (first_skipped == -1) first_skipped = i;
    557       continue;
    558     }
    559     if (key->FilterKey(keys->filter())) continue;
    560 
    561     if (is_shadowing_key) {
    562       keys->AddShadowingKey(key);
    563     } else {
    564       keys->AddKey(key, DO_NOT_CONVERT);
    565     }
    566   }
    567   return first_skipped;
    568 }
    569 
    570 template <class T>
    571 Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
    572                                                     KeyCollectionMode mode,
    573                                                     KeyAccumulator* accumulator,
    574                                                     Handle<JSObject> object,
    575                                                     T* raw_dictionary) {
    576   Handle<T> dictionary(raw_dictionary, isolate);
    577   int length = dictionary->NumberOfEnumElements();
    578   if (length == 0) {
    579     return isolate->factory()->empty_fixed_array();
    580   }
    581   Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
    582   T::CopyEnumKeysTo(dictionary, storage, mode, accumulator);
    583   return storage;
    584 }
    585 }  // namespace
    586 
    587 Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
    588                                                     Handle<JSObject> object) {
    589   if (filter_ == ENUMERABLE_STRINGS) {
    590     Handle<FixedArray> enum_keys;
    591     if (object->HasFastProperties()) {
    592       enum_keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, object);
    593       // If the number of properties equals the length of enumerable properties
    594       // we do not have to filter out non-enumerable ones
    595       Map* map = object->map();
    596       int nof_descriptors = map->NumberOfOwnDescriptors();
    597       if (enum_keys->length() != nof_descriptors) {
    598         Handle<DescriptorArray> descs =
    599             Handle<DescriptorArray>(map->instance_descriptors(), isolate_);
    600         for (int i = 0; i < nof_descriptors; i++) {
    601           PropertyDetails details = descs->GetDetails(i);
    602           if (!details.IsDontEnum()) continue;
    603           Object* key = descs->GetKey(i);
    604           this->AddShadowingKey(key);
    605         }
    606       }
    607     } else if (object->IsJSGlobalObject()) {
    608       enum_keys = GetOwnEnumPropertyDictionaryKeys(
    609           isolate_, mode_, this, object, object->global_dictionary());
    610     } else {
    611       enum_keys = GetOwnEnumPropertyDictionaryKeys(
    612           isolate_, mode_, this, object, object->property_dictionary());
    613     }
    614     AddKeys(enum_keys, DO_NOT_CONVERT);
    615   } else {
    616     if (object->HasFastProperties()) {
    617       int limit = object->map()->NumberOfOwnDescriptors();
    618       Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
    619                                     isolate_);
    620       // First collect the strings,
    621       int first_symbol =
    622           CollectOwnPropertyNamesInternal<true>(object, this, descs, 0, limit);
    623       // then the symbols.
    624       if (first_symbol != -1) {
    625         CollectOwnPropertyNamesInternal<false>(object, this, descs,
    626                                                first_symbol, limit);
    627       }
    628     } else if (object->IsJSGlobalObject()) {
    629       GlobalDictionary::CollectKeysTo(
    630           handle(object->global_dictionary(), isolate_), this);
    631     } else {
    632       NameDictionary::CollectKeysTo(
    633           handle(object->property_dictionary(), isolate_), this);
    634     }
    635   }
    636   // Add the property keys from the interceptor.
    637   return CollectInterceptorKeys<v8::GenericNamedPropertyEnumeratorCallback,
    638                                 kNamed>(receiver, object, this);
    639 }
    640 
    641 Maybe<bool> KeyAccumulator::CollectAccessCheckInterceptorKeys(
    642     Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
    643     Handle<JSObject> object) {
    644   MAYBE_RETURN(
    645       (CollectInterceptorKeysInternal<v8::IndexedPropertyEnumeratorCallback,
    646                                       kIndexed>(
    647           receiver, object,
    648           handle(
    649               InterceptorInfo::cast(access_check_info->indexed_interceptor()),
    650               isolate_),
    651           this)),
    652       Nothing<bool>());
    653   MAYBE_RETURN(
    654       (CollectInterceptorKeysInternal<
    655           v8::GenericNamedPropertyEnumeratorCallback, kNamed>(
    656           receiver, object,
    657           handle(InterceptorInfo::cast(access_check_info->named_interceptor()),
    658                  isolate_),
    659           this)),
    660       Nothing<bool>());
    661   return Just(true);
    662 }
    663 
    664 // Returns |true| on success, |false| if prototype walking should be stopped,
    665 // |nothing| if an exception was thrown.
    666 Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
    667                                            Handle<JSObject> object) {
    668   // Check access rights if required.
    669   if (object->IsAccessCheckNeeded() &&
    670       !isolate_->MayAccess(handle(isolate_->context()), object)) {
    671     // The cross-origin spec says that [[Enumerate]] shall return an empty
    672     // iterator when it doesn't have access...
    673     if (mode_ == KeyCollectionMode::kIncludePrototypes) {
    674       return Just(false);
    675     }
    676     // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
    677     DCHECK(KeyCollectionMode::kOwnOnly == mode_);
    678     Handle<AccessCheckInfo> access_check_info;
    679     {
    680       DisallowHeapAllocation no_gc;
    681       AccessCheckInfo* maybe_info = AccessCheckInfo::Get(isolate_, object);
    682       if (maybe_info) access_check_info = handle(maybe_info, isolate_);
    683     }
    684     // We always have both kinds of interceptors or none.
    685     if (!access_check_info.is_null() &&
    686         access_check_info->named_interceptor()) {
    687       MAYBE_RETURN(CollectAccessCheckInterceptorKeys(access_check_info,
    688                                                      receiver, object),
    689                    Nothing<bool>());
    690       return Just(false);
    691     }
    692     filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
    693   }
    694   MAYBE_RETURN(CollectOwnElementIndices(receiver, object), Nothing<bool>());
    695   MAYBE_RETURN(CollectOwnPropertyNames(receiver, object), Nothing<bool>());
    696   return Just(true);
    697 }
    698 
    699 // static
    700 Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
    701     Isolate* isolate, Handle<JSObject> object) {
    702   if (object->HasFastProperties()) {
    703     return GetFastEnumPropertyKeys(isolate, object);
    704   } else if (object->IsJSGlobalObject()) {
    705     return GetOwnEnumPropertyDictionaryKeys(
    706         isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
    707         object->global_dictionary());
    708   } else {
    709     return GetOwnEnumPropertyDictionaryKeys(
    710         isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
    711         object->property_dictionary());
    712   }
    713 }
    714 
    715 // ES6 9.5.12
    716 // Returns |true| on success, |nothing| in case of exception.
    717 Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
    718                                                   Handle<JSProxy> proxy) {
    719   STACK_CHECK(isolate_, Nothing<bool>());
    720   // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
    721   Handle<Object> handler(proxy->handler(), isolate_);
    722   // 2. If handler is null, throw a TypeError exception.
    723   // 3. Assert: Type(handler) is Object.
    724   if (proxy->IsRevoked()) {
    725     isolate_->Throw(*isolate_->factory()->NewTypeError(
    726         MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string()));
    727     return Nothing<bool>();
    728   }
    729   // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
    730   Handle<JSReceiver> target(proxy->target(), isolate_);
    731   // 5. Let trap be ? GetMethod(handler, "ownKeys").
    732   Handle<Object> trap;
    733   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    734       isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
    735                                         isolate_->factory()->ownKeys_string()),
    736       Nothing<bool>());
    737   // 6. If trap is undefined, then
    738   if (trap->IsUndefined(isolate_)) {
    739     // 6a. Return target.[[OwnPropertyKeys]]().
    740     return CollectOwnJSProxyTargetKeys(proxy, target);
    741   }
    742   // 7. Let trapResultArray be Call(trap, handler, target).
    743   Handle<Object> trap_result_array;
    744   Handle<Object> args[] = {target};
    745   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    746       isolate_, trap_result_array,
    747       Execution::Call(isolate_, trap, handler, arraysize(args), args),
    748       Nothing<bool>());
    749   // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
    750   //    String, Symbol).
    751   Handle<FixedArray> trap_result;
    752   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    753       isolate_, trap_result,
    754       Object::CreateListFromArrayLike(isolate_, trap_result_array,
    755                                       ElementTypes::kStringAndSymbol),
    756       Nothing<bool>());
    757   // 9. Let extensibleTarget be ? IsExtensible(target).
    758   Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
    759   MAYBE_RETURN(maybe_extensible, Nothing<bool>());
    760   bool extensible_target = maybe_extensible.FromJust();
    761   // 10. Let targetKeys be ? target.[[OwnPropertyKeys]]().
    762   Handle<FixedArray> target_keys;
    763   ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys,
    764                                    JSReceiver::OwnPropertyKeys(target),
    765                                    Nothing<bool>());
    766   // 11. (Assert)
    767   // 12. Let targetConfigurableKeys be an empty List.
    768   // To save memory, we're re-using target_keys and will modify it in-place.
    769   Handle<FixedArray> target_configurable_keys = target_keys;
    770   // 13. Let targetNonconfigurableKeys be an empty List.
    771   Handle<FixedArray> target_nonconfigurable_keys =
    772       isolate_->factory()->NewFixedArray(target_keys->length());
    773   int nonconfigurable_keys_length = 0;
    774   // 14. Repeat, for each element key of targetKeys:
    775   for (int i = 0; i < target_keys->length(); ++i) {
    776     // 14a. Let desc be ? target.[[GetOwnProperty]](key).
    777     PropertyDescriptor desc;
    778     Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
    779         isolate_, target, handle(target_keys->get(i), isolate_), &desc);
    780     MAYBE_RETURN(found, Nothing<bool>());
    781     // 14b. If desc is not undefined and desc.[[Configurable]] is false, then
    782     if (found.FromJust() && !desc.configurable()) {
    783       // 14b i. Append key as an element of targetNonconfigurableKeys.
    784       target_nonconfigurable_keys->set(nonconfigurable_keys_length,
    785                                        target_keys->get(i));
    786       nonconfigurable_keys_length++;
    787       // The key was moved, null it out in the original list.
    788       target_keys->set(i, Smi::kZero);
    789     } else {
    790       // 14c. Else,
    791       // 14c i. Append key as an element of targetConfigurableKeys.
    792       // (No-op, just keep it in |target_keys|.)
    793     }
    794   }
    795   // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty,
    796   //     then:
    797   if (extensible_target && nonconfigurable_keys_length == 0) {
    798     // 15a. Return trapResult.
    799     return AddKeysFromJSProxy(proxy, trap_result);
    800   }
    801   // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult.
    802   Zone set_zone(isolate_->allocator(), ZONE_NAME);
    803   const int kPresent = 1;
    804   const int kGone = 0;
    805   IdentityMap<int, ZoneAllocationPolicy> unchecked_result_keys(
    806       isolate_->heap(), ZoneAllocationPolicy(&set_zone));
    807   int unchecked_result_keys_size = 0;
    808   for (int i = 0; i < trap_result->length(); ++i) {
    809     DCHECK(trap_result->get(i)->IsUniqueName());
    810     Object* key = trap_result->get(i);
    811     int* entry = unchecked_result_keys.Get(key);
    812     if (*entry != kPresent) {
    813       *entry = kPresent;
    814       unchecked_result_keys_size++;
    815     }
    816   }
    817   // 17. Repeat, for each key that is an element of targetNonconfigurableKeys:
    818   for (int i = 0; i < nonconfigurable_keys_length; ++i) {
    819     Object* key = target_nonconfigurable_keys->get(i);
    820     // 17a. If key is not an element of uncheckedResultKeys, throw a
    821     //      TypeError exception.
    822     int* found = unchecked_result_keys.Find(key);
    823     if (found == nullptr || *found == kGone) {
    824       isolate_->Throw(*isolate_->factory()->NewTypeError(
    825           MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_)));
    826       return Nothing<bool>();
    827     }
    828     // 17b. Remove key from uncheckedResultKeys.
    829     *found = kGone;
    830     unchecked_result_keys_size--;
    831   }
    832   // 18. If extensibleTarget is true, return trapResult.
    833   if (extensible_target) {
    834     return AddKeysFromJSProxy(proxy, trap_result);
    835   }
    836   // 19. Repeat, for each key that is an element of targetConfigurableKeys:
    837   for (int i = 0; i < target_configurable_keys->length(); ++i) {
    838     Object* key = target_configurable_keys->get(i);
    839     if (key->IsSmi()) continue;  // Zapped entry, was nonconfigurable.
    840     // 19a. If key is not an element of uncheckedResultKeys, throw a
    841     //      TypeError exception.
    842     int* found = unchecked_result_keys.Find(key);
    843     if (found == nullptr || *found == kGone) {
    844       isolate_->Throw(*isolate_->factory()->NewTypeError(
    845           MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_)));
    846       return Nothing<bool>();
    847     }
    848     // 19b. Remove key from uncheckedResultKeys.
    849     *found = kGone;
    850     unchecked_result_keys_size--;
    851   }
    852   // 20. If uncheckedResultKeys is not empty, throw a TypeError exception.
    853   if (unchecked_result_keys_size != 0) {
    854     DCHECK_GT(unchecked_result_keys_size, 0);
    855     isolate_->Throw(*isolate_->factory()->NewTypeError(
    856         MessageTemplate::kProxyOwnKeysNonExtensible));
    857     return Nothing<bool>();
    858   }
    859   // 21. Return trapResult.
    860   return AddKeysFromJSProxy(proxy, trap_result);
    861 }
    862 
    863 Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys(
    864     Handle<JSProxy> proxy, Handle<JSReceiver> target) {
    865   // TODO(cbruni): avoid creating another KeyAccumulator
    866   Handle<FixedArray> keys;
    867   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    868       isolate_, keys,
    869       KeyAccumulator::GetKeys(target, KeyCollectionMode::kOwnOnly, filter_,
    870                               GetKeysConversion::kConvertToString, is_for_in_),
    871       Nothing<bool>());
    872   Maybe<bool> result = AddKeysFromJSProxy(proxy, keys);
    873   return result;
    874 }
    875 
    876 }  // namespace internal
    877 }  // namespace v8
    878