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