Merge V8 5.2.361.47  DO NOT MERGE

https://chromium.googlesource.com/v8/v8/+/5.2.361.47

FPIIM-449

Change-Id: Ibec421b85a9b88cb3a432ada642e469fe7e78346
(cherry picked from commit bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8)
diff --git a/src/keys.cc b/src/keys.cc
index f8b606c..17270eb 100644
--- a/src/keys.cc
+++ b/src/keys.cc
@@ -4,8 +4,10 @@
 
 #include "src/keys.h"
 
+#include "src/api-arguments.h"
 #include "src/elements.h"
 #include "src/factory.h"
+#include "src/identity-map.h"
 #include "src/isolate-inl.h"
 #include "src/objects-inl.h"
 #include "src/property-descriptor.h"
@@ -20,6 +22,33 @@
   }
 }
 
+namespace {
+
+static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
+  int len = array->length();
+  for (int i = 0; i < len; i++) {
+    Object* e = array->get(i);
+    if (!(e->IsName() || e->IsNumber())) return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
+    Handle<JSReceiver> object, KeyCollectionType type, PropertyFilter filter,
+    GetKeysConversion keys_conversion, bool filter_proxy_keys) {
+  USE(ContainsOnlyValidKeys);
+  Isolate* isolate = object->GetIsolate();
+  KeyAccumulator accumulator(isolate, type, filter);
+  accumulator.set_filter_proxy_keys(filter_proxy_keys);
+  MAYBE_RETURN(accumulator.CollectKeys(object, object),
+               MaybeHandle<FixedArray>());
+  Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion);
+  DCHECK(ContainsOnlyValidKeys(keys));
+  return keys;
+}
+
 Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
   if (length_ == 0) {
     return isolate_->factory()->empty_fixed_array();
@@ -111,7 +140,7 @@
     return AddSymbolKey(key);
   }
   if (filter_ & SKIP_STRINGS) return false;
-  // Make sure we do not add keys to a proxy-level (see AddKeysFromProxy).
+  // Make sure we do not add keys to a proxy-level (see AddKeysFromJSProxy).
   DCHECK_LE(0, level_string_length_);
   // 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
@@ -143,7 +172,7 @@
 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).
+  // Make sure we do not add keys to a proxy-level (see AddKeysFromJSProxy).
   // We mark proxy-levels with a negative length
   DCHECK_LE(0, level_string_length_);
   // Binary search over all but the last level. The last one might not be
@@ -209,17 +238,6 @@
   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
-  // KeyAccumulator.
-  AddKeys(array_like, PROXY_MAGIC);
-  // Invert the current length to indicate a present proxy, so we can ignore
-  // element keys for this level. Otherwise we would not fully respect the order
-  // given by the proxy.
-  level_string_length_ = -level_string_length_;
-}
-
 MaybeHandle<FixedArray> FilterProxyKeys(Isolate* isolate, Handle<JSProxy> owner,
                                         Handle<FixedArray> keys,
                                         PropertyFilter filter) {
@@ -250,8 +268,8 @@
 }
 
 // Returns "nothing" in case of exception, "true" on success.
-Maybe<bool> KeyAccumulator::AddKeysFromProxy(Handle<JSProxy> proxy,
-                                             Handle<FixedArray> keys) {
+Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy,
+                                               Handle<FixedArray> keys) {
   if (filter_proxy_keys_) {
     ASSIGN_RETURN_ON_EXCEPTION_VALUE(
         isolate_, keys, FilterProxyKeys(isolate_, proxy, keys, filter_),
@@ -312,6 +330,43 @@
   level_symbol_length_ = 0;
 }
 
+Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
+                                        Handle<JSReceiver> object) {
+  // Proxies have no hidden prototype and we should not trigger the
+  // [[GetPrototypeOf]] trap on the last iteration when using
+  // AdvanceFollowingProxies.
+  if (type_ == OWN_ONLY && object->IsJSProxy()) {
+    MAYBE_RETURN(CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(object)),
+                 Nothing<bool>());
+    return Just(true);
+  }
+
+  PrototypeIterator::WhereToEnd end = type_ == OWN_ONLY
+                                          ? PrototypeIterator::END_AT_NON_HIDDEN
+                                          : PrototypeIterator::END_AT_NULL;
+  for (PrototypeIterator iter(isolate_, object,
+                              PrototypeIterator::START_AT_RECEIVER, end);
+       !iter.IsAtEnd();) {
+    Handle<JSReceiver> current =
+        PrototypeIterator::GetCurrent<JSReceiver>(iter);
+    Maybe<bool> result = Just(false);  // Dummy initialization.
+    if (current->IsJSProxy()) {
+      result = CollectOwnJSProxyKeys(receiver, Handle<JSProxy>::cast(current));
+    } else {
+      DCHECK(current->IsJSObject());
+      result = CollectOwnKeys(receiver, Handle<JSObject>::cast(current));
+    }
+    MAYBE_RETURN(result, Nothing<bool>());
+    if (!result.FromJust()) break;  // |false| means "stop iterating".
+    // Iterate through proxies but ignore access checks for the ALL_CAN_READ
+    // case on API objects for OWN_ONLY keys handled in CollectOwnKeys.
+    if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
+      return Nothing<bool>();
+    }
+  }
+  return Just(true);
+}
+
 namespace {
 
 void TrySettingEmptyEnumCache(JSReceiver* object) {
@@ -363,6 +418,89 @@
 }
 
 namespace {
+static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
+                                             Handle<FixedArray> array,
+                                             int length) {
+  DCHECK_LE(length, array->length());
+  if (array->length() == length) return array;
+  return isolate->factory()->CopyFixedArrayUpTo(array, length);
+}
+
+Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
+                                           Handle<JSObject> object) {
+  Handle<Map> map(object->map());
+  bool cache_enum_length = map->OnlyHasSimpleProperties();
+
+  Handle<DescriptorArray> descs =
+      Handle<DescriptorArray>(map->instance_descriptors(), isolate);
+  int own_property_count = map->EnumLength();
+  // If the enum length of the given map is set to kInvalidEnumCache, this
+  // means that the map itself has never used the present enum cache. The
+  // first step to using the cache is to set the enum length of the map by
+  // counting the number of own descriptors that are ENUMERABLE_STRINGS.
+  if (own_property_count == kInvalidEnumCacheSentinel) {
+    own_property_count =
+        map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS);
+  } else {
+    DCHECK(
+        own_property_count ==
+        map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS));
+  }
+
+  if (descs->HasEnumCache()) {
+    Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
+    // In case the number of properties required in the enum are actually
+    // present, we can reuse the enum cache. Otherwise, this means that the
+    // enum cache was generated for a previous (smaller) version of the
+    // Descriptor Array. In that case we regenerate the enum cache.
+    if (own_property_count <= keys->length()) {
+      isolate->counters()->enum_cache_hits()->Increment();
+      if (cache_enum_length) map->SetEnumLength(own_property_count);
+      return ReduceFixedArrayTo(isolate, keys, own_property_count);
+    }
+  }
+
+  if (descs->IsEmpty()) {
+    isolate->counters()->enum_cache_hits()->Increment();
+    if (cache_enum_length) map->SetEnumLength(0);
+    return isolate->factory()->empty_fixed_array();
+  }
+
+  isolate->counters()->enum_cache_misses()->Increment();
+
+  Handle<FixedArray> storage =
+      isolate->factory()->NewFixedArray(own_property_count);
+  Handle<FixedArray> indices =
+      isolate->factory()->NewFixedArray(own_property_count);
+
+  int size = map->NumberOfOwnDescriptors();
+  int index = 0;
+
+  for (int i = 0; i < size; i++) {
+    PropertyDetails details = descs->GetDetails(i);
+    if (details.IsDontEnum()) continue;
+    Object* key = descs->GetKey(i);
+    if (key->IsSymbol()) continue;
+    storage->set(index, key);
+    if (!indices.is_null()) {
+      if (details.type() != DATA) {
+        indices = Handle<FixedArray>();
+      } else {
+        FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
+        int load_by_field_index = field_index.GetLoadByFieldIndex();
+        indices->set(index, Smi::FromInt(load_by_field_index));
+      }
+    }
+    index++;
+  }
+  DCHECK(index == storage->length());
+
+  DescriptorArray::SetEnumCache(descs, isolate, storage, indices);
+  if (cache_enum_length) {
+    map->SetEnumLength(own_property_count);
+  }
+  return storage;
+}
 
 template <bool fast_properties>
 Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
@@ -371,10 +509,10 @@
   Handle<FixedArray> keys;
   ElementsAccessor* accessor = object->GetElementsAccessor();
   if (fast_properties) {
-    keys = JSObject::GetFastEnumPropertyKeys(isolate, object);
+    keys = GetFastEnumPropertyKeys(isolate, object);
   } else {
     // TODO(cbruni): preallocate big enough array to also hold elements.
-    keys = JSObject::GetEnumPropertyKeys(object);
+    keys = KeyAccumulator::GetEnumPropertyKeys(isolate, object);
   }
   Handle<FixedArray> result =
       accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
@@ -402,7 +540,7 @@
   }
   // We have no elements but possibly enumerable property keys, hence we can
   // directly initialize the enum cache.
-  return JSObject::GetFastEnumPropertyKeys(isolate, object);
+  return GetFastEnumPropertyKeys(isolate, object);
 }
 
 bool OnlyHasSimpleProperties(Map* map) {
@@ -457,9 +595,314 @@
 
 MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
     GetKeysConversion convert) {
-  return JSReceiver::GetKeys(receiver_, type_, ENUMERABLE_STRINGS, KEEP_NUMBERS,
+  return JSReceiver::GetKeys(receiver_, type_, filter_, KEEP_NUMBERS,
                              filter_proxy_keys_);
 }
 
+enum IndexedOrNamed { kIndexed, kNamed };
+
+// Returns |true| on success, |nothing| on exception.
+template <class Callback, IndexedOrNamed type>
+static Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver,
+                                          Handle<JSObject> object,
+                                          KeyAccumulator* accumulator) {
+  Isolate* isolate = accumulator->isolate();
+  if (type == kIndexed) {
+    if (!object->HasIndexedInterceptor()) return Just(true);
+  } else {
+    if (!object->HasNamedInterceptor()) return Just(true);
+  }
+  Handle<InterceptorInfo> interceptor(type == kIndexed
+                                          ? object->GetIndexedInterceptor()
+                                          : object->GetNamedInterceptor(),
+                                      isolate);
+  if ((accumulator->filter() & ONLY_ALL_CAN_READ) &&
+      !interceptor->all_can_read()) {
+    return Just(true);
+  }
+  PropertyCallbackArguments args(isolate, interceptor->data(), *receiver,
+                                 *object, Object::DONT_THROW);
+  Handle<JSObject> result;
+  if (!interceptor->enumerator()->IsUndefined()) {
+    Callback enum_fun = v8::ToCData<Callback>(interceptor->enumerator());
+    const char* log_tag = type == kIndexed ? "interceptor-indexed-enum"
+                                           : "interceptor-named-enum";
+    LOG(isolate, ApiObjectAccess(log_tag, *object));
+    result = args.Call(enum_fun);
+  }
+  RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate, Nothing<bool>());
+  if (result.is_null()) return Just(true);
+  DCHECK(result->IsJSArray() || result->HasSloppyArgumentsElements());
+  // The accumulator takes care of string/symbol filtering.
+  if (type == kIndexed) {
+    accumulator->AddElementKeysFromInterceptor(result);
+  } else {
+    accumulator->AddKeys(result, DO_NOT_CONVERT);
+  }
+  return Just(true);
+}
+
+void KeyAccumulator::CollectOwnElementIndices(Handle<JSObject> object) {
+  if (filter_ & SKIP_STRINGS) return;
+  ElementsAccessor* accessor = object->GetElementsAccessor();
+  accessor->CollectElementIndices(object, this);
+}
+
+void KeyAccumulator::CollectOwnPropertyNames(Handle<JSObject> object) {
+  if (object->HasFastProperties()) {
+    int real_size = object->map()->NumberOfOwnDescriptors();
+    Handle<DescriptorArray> descs(object->map()->instance_descriptors(),
+                                  isolate_);
+    for (int i = 0; i < real_size; i++) {
+      PropertyDetails details = descs->GetDetails(i);
+      if ((details.attributes() & filter_) != 0) continue;
+      if (filter_ & ONLY_ALL_CAN_READ) {
+        if (details.kind() != kAccessor) continue;
+        Object* accessors = descs->GetValue(i);
+        if (!accessors->IsAccessorInfo()) continue;
+        if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
+      }
+      Name* key = descs->GetKey(i);
+      if (key->FilterKey(filter_)) continue;
+      AddKey(key, DO_NOT_CONVERT);
+    }
+  } else if (object->IsJSGlobalObject()) {
+    GlobalDictionary::CollectKeysTo(
+        handle(object->global_dictionary(), isolate_), this, filter_);
+  } else {
+    NameDictionary::CollectKeysTo(
+        handle(object->property_dictionary(), isolate_), this, filter_);
+  }
+}
+
+// Returns |true| on success, |false| if prototype walking should be stopped,
+// |nothing| if an exception was thrown.
+Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
+                                           Handle<JSObject> object) {
+  NextPrototype();
+  // Check access rights if required.
+  if (object->IsAccessCheckNeeded() &&
+      !isolate_->MayAccess(handle(isolate_->context()), object)) {
+    // The cross-origin spec says that [[Enumerate]] shall return an empty
+    // iterator when it doesn't have access...
+    if (type_ == INCLUDE_PROTOS) {
+      return Just(false);
+    }
+    // ...whereas [[OwnPropertyKeys]] shall return whitelisted properties.
+    DCHECK_EQ(OWN_ONLY, type_);
+    filter_ = static_cast<PropertyFilter>(filter_ | ONLY_ALL_CAN_READ);
+  }
+
+  CollectOwnElementIndices(object);
+
+  // Add the element keys from the interceptor.
+  Maybe<bool> success =
+      GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>(
+          receiver, object, this);
+  MAYBE_RETURN(success, Nothing<bool>());
+
+  if (filter_ == ENUMERABLE_STRINGS) {
+    Handle<FixedArray> enum_keys =
+        KeyAccumulator::GetEnumPropertyKeys(isolate_, object);
+    AddKeys(enum_keys, DO_NOT_CONVERT);
+  } else {
+    CollectOwnPropertyNames(object);
+  }
+
+  // Add the property keys from the interceptor.
+  success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback,
+                                   kNamed>(receiver, object, this);
+  MAYBE_RETURN(success, Nothing<bool>());
+  return Just(true);
+}
+
+// static
+Handle<FixedArray> KeyAccumulator::GetEnumPropertyKeys(
+    Isolate* isolate, Handle<JSObject> object) {
+  if (object->HasFastProperties()) {
+    return GetFastEnumPropertyKeys(isolate, object);
+  } else if (object->IsJSGlobalObject()) {
+    Handle<GlobalDictionary> dictionary(object->global_dictionary(), isolate);
+    int length = dictionary->NumberOfEnumElements();
+    if (length == 0) {
+      return isolate->factory()->empty_fixed_array();
+    }
+    Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
+    dictionary->CopyEnumKeysTo(*storage);
+    return storage;
+  } else {
+    Handle<NameDictionary> dictionary(object->property_dictionary(), isolate);
+    int length = dictionary->NumberOfEnumElements();
+    if (length == 0) {
+      return isolate->factory()->empty_fixed_array();
+    }
+    Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
+    dictionary->CopyEnumKeysTo(*storage);
+    return storage;
+  }
+}
+
+// ES6 9.5.12
+// Returns |true| on success, |nothing| in case of exception.
+Maybe<bool> KeyAccumulator::CollectOwnJSProxyKeys(Handle<JSReceiver> receiver,
+                                                  Handle<JSProxy> proxy) {
+  STACK_CHECK(isolate_, Nothing<bool>());
+  // 1. Let handler be the value of the [[ProxyHandler]] internal slot of O.
+  Handle<Object> handler(proxy->handler(), isolate_);
+  // 2. If handler is null, throw a TypeError exception.
+  // 3. Assert: Type(handler) is Object.
+  if (proxy->IsRevoked()) {
+    isolate_->Throw(*isolate_->factory()->NewTypeError(
+        MessageTemplate::kProxyRevoked, isolate_->factory()->ownKeys_string()));
+    return Nothing<bool>();
+  }
+  // 4. Let target be the value of the [[ProxyTarget]] internal slot of O.
+  Handle<JSReceiver> target(proxy->target(), isolate_);
+  // 5. Let trap be ? GetMethod(handler, "ownKeys").
+  Handle<Object> trap;
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+      isolate_, trap, Object::GetMethod(Handle<JSReceiver>::cast(handler),
+                                        isolate_->factory()->ownKeys_string()),
+      Nothing<bool>());
+  // 6. If trap is undefined, then
+  if (trap->IsUndefined()) {
+    // 6a. Return target.[[OwnPropertyKeys]]().
+    return CollectOwnJSProxyTargetKeys(proxy, target);
+  }
+  // 7. Let trapResultArray be Call(trap, handler, «target»).
+  Handle<Object> trap_result_array;
+  Handle<Object> args[] = {target};
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+      isolate_, trap_result_array,
+      Execution::Call(isolate_, trap, handler, arraysize(args), args),
+      Nothing<bool>());
+  // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray,
+  //    «String, Symbol»).
+  Handle<FixedArray> trap_result;
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+      isolate_, trap_result,
+      Object::CreateListFromArrayLike(isolate_, trap_result_array,
+                                      ElementTypes::kStringAndSymbol),
+      Nothing<bool>());
+  // 9. Let extensibleTarget be ? IsExtensible(target).
+  Maybe<bool> maybe_extensible = JSReceiver::IsExtensible(target);
+  MAYBE_RETURN(maybe_extensible, Nothing<bool>());
+  bool extensible_target = maybe_extensible.FromJust();
+  // 10. Let targetKeys be ? target.[[OwnPropertyKeys]]().
+  Handle<FixedArray> target_keys;
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, target_keys,
+                                   JSReceiver::OwnPropertyKeys(target),
+                                   Nothing<bool>());
+  // 11. (Assert)
+  // 12. Let targetConfigurableKeys be an empty List.
+  // To save memory, we're re-using target_keys and will modify it in-place.
+  Handle<FixedArray> target_configurable_keys = target_keys;
+  // 13. Let targetNonconfigurableKeys be an empty List.
+  Handle<FixedArray> target_nonconfigurable_keys =
+      isolate_->factory()->NewFixedArray(target_keys->length());
+  int nonconfigurable_keys_length = 0;
+  // 14. Repeat, for each element key of targetKeys:
+  for (int i = 0; i < target_keys->length(); ++i) {
+    // 14a. Let desc be ? target.[[GetOwnProperty]](key).
+    PropertyDescriptor desc;
+    Maybe<bool> found = JSReceiver::GetOwnPropertyDescriptor(
+        isolate_, target, handle(target_keys->get(i), isolate_), &desc);
+    MAYBE_RETURN(found, Nothing<bool>());
+    // 14b. If desc is not undefined and desc.[[Configurable]] is false, then
+    if (found.FromJust() && !desc.configurable()) {
+      // 14b i. Append key as an element of targetNonconfigurableKeys.
+      target_nonconfigurable_keys->set(nonconfigurable_keys_length,
+                                       target_keys->get(i));
+      nonconfigurable_keys_length++;
+      // The key was moved, null it out in the original list.
+      target_keys->set(i, Smi::FromInt(0));
+    } else {
+      // 14c. Else,
+      // 14c i. Append key as an element of targetConfigurableKeys.
+      // (No-op, just keep it in |target_keys|.)
+    }
+  }
+  NextPrototype();  // Prepare for accumulating keys.
+  // 15. If extensibleTarget is true and targetNonconfigurableKeys is empty,
+  //     then:
+  if (extensible_target && nonconfigurable_keys_length == 0) {
+    // 15a. Return trapResult.
+    return AddKeysFromJSProxy(proxy, trap_result);
+  }
+  // 16. Let uncheckedResultKeys be a new List which is a copy of trapResult.
+  Zone set_zone(isolate_->allocator());
+  const int kPresent = 1;
+  const int kGone = 0;
+  IdentityMap<int> unchecked_result_keys(isolate_->heap(), &set_zone);
+  int unchecked_result_keys_size = 0;
+  for (int i = 0; i < trap_result->length(); ++i) {
+    DCHECK(trap_result->get(i)->IsUniqueName());
+    Object* key = trap_result->get(i);
+    int* entry = unchecked_result_keys.Get(key);
+    if (*entry != kPresent) {
+      *entry = kPresent;
+      unchecked_result_keys_size++;
+    }
+  }
+  // 17. Repeat, for each key that is an element of targetNonconfigurableKeys:
+  for (int i = 0; i < nonconfigurable_keys_length; ++i) {
+    Object* key = target_nonconfigurable_keys->get(i);
+    // 17a. If key is not an element of uncheckedResultKeys, throw a
+    //      TypeError exception.
+    int* found = unchecked_result_keys.Find(key);
+    if (found == nullptr || *found == kGone) {
+      isolate_->Throw(*isolate_->factory()->NewTypeError(
+          MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_)));
+      return Nothing<bool>();
+    }
+    // 17b. Remove key from uncheckedResultKeys.
+    *found = kGone;
+    unchecked_result_keys_size--;
+  }
+  // 18. If extensibleTarget is true, return trapResult.
+  if (extensible_target) {
+    return AddKeysFromJSProxy(proxy, trap_result);
+  }
+  // 19. Repeat, for each key that is an element of targetConfigurableKeys:
+  for (int i = 0; i < target_configurable_keys->length(); ++i) {
+    Object* key = target_configurable_keys->get(i);
+    if (key->IsSmi()) continue;  // Zapped entry, was nonconfigurable.
+    // 19a. If key is not an element of uncheckedResultKeys, throw a
+    //      TypeError exception.
+    int* found = unchecked_result_keys.Find(key);
+    if (found == nullptr || *found == kGone) {
+      isolate_->Throw(*isolate_->factory()->NewTypeError(
+          MessageTemplate::kProxyOwnKeysMissing, handle(key, isolate_)));
+      return Nothing<bool>();
+    }
+    // 19b. Remove key from uncheckedResultKeys.
+    *found = kGone;
+    unchecked_result_keys_size--;
+  }
+  // 20. If uncheckedResultKeys is not empty, throw a TypeError exception.
+  if (unchecked_result_keys_size != 0) {
+    DCHECK_GT(unchecked_result_keys_size, 0);
+    isolate_->Throw(*isolate_->factory()->NewTypeError(
+        MessageTemplate::kProxyOwnKeysNonExtensible));
+    return Nothing<bool>();
+  }
+  // 21. Return trapResult.
+  return AddKeysFromJSProxy(proxy, trap_result);
+}
+
+Maybe<bool> KeyAccumulator::CollectOwnJSProxyTargetKeys(
+    Handle<JSProxy> proxy, Handle<JSReceiver> target) {
+  // TODO(cbruni): avoid creating another KeyAccumulator
+  Handle<FixedArray> keys;
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+      isolate_, keys, JSReceiver::OwnPropertyKeys(target), Nothing<bool>());
+  NextPrototype();  // Prepare for accumulating keys.
+  bool prev_filter_proxy_keys_ = filter_proxy_keys_;
+  filter_proxy_keys_ = false;
+  Maybe<bool> result = AddKeysFromJSProxy(proxy, keys);
+  filter_proxy_keys_ = prev_filter_proxy_keys_;
+  return result;
+}
+
 }  // namespace internal
 }  // namespace v8