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

[keys] Moving property/keys related methods to KeyAccumulator in keys.cc

The Great Keys Migration:
This is part of a bigger effort to centralize optimizations for key collections
in a central place. This necessary to avoid the penalty that would be introduced
by fixing shadowed property iteration.

BUG=v8:4758, v8:705
LOG=N

Review-Url: https://codereview.chromium.org/1938413002
Cr-Commit-Position: refs/heads/master@{#35991}
parent 21ce625b
......@@ -1482,6 +1482,15 @@ class StackLimitCheck BASE_EMBEDDED {
Isolate* isolate_;
};
#define STACK_CHECK(isolate, result_value) \
do { \
StackLimitCheck stack_check(isolate); \
if (stack_check.HasOverflowed()) { \
isolate->Throw(*isolate->factory()->NewRangeError( \
MessageTemplate::kStackOverflow)); \
return result_value; \
} \
} while (false)
// Support for temporarily postponing interrupts. When the outermost
// postpone scope is left the interrupts will be re-enabled and any
......
......@@ -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"
......@@ -312,6 +314,46 @@ void KeyAccumulator::NextPrototype() {
level_symbol_length_ = 0;
}
Maybe<bool> KeyAccumulator::GetKeys_Internal(Handle<JSReceiver> receiver,
Handle<JSReceiver> object,
KeyCollectionType type) {
// 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(
JSProxyOwnPropertyKeys(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 = JSProxyOwnPropertyKeys(receiver, Handle<JSProxy>::cast(current));
} else {
DCHECK(current->IsJSObject());
result =
GetKeysFromJSObject(receiver, Handle<JSObject>::cast(current), type);
}
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 GetKeysFromJSObject.
if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
return Nothing<bool>();
}
}
return Just(true);
}
namespace {
void TrySettingEmptyEnumCache(JSReceiver* object) {
......@@ -363,6 +405,89 @@ void FastKeyAccumulator::Prepare() {
}
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 +496,10 @@ Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
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 +527,7 @@ MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache(
}
// 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) {
......@@ -461,5 +586,297 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
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::CollectOwnElementKeys(Handle<JSObject> object) {
if (filter_ & SKIP_STRINGS) return;
ElementsAccessor* accessor = object->GetElementsAccessor();
accessor->CollectElementIndices(object, this, kMaxUInt32, filter_, 0);
}
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;
this->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::GetKeysFromJSObject(Handle<JSReceiver> receiver,
Handle<JSObject> object,
KeyCollectionType type) {
this->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);
}
this->CollectOwnElementKeys(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);
this->AddKeys(enum_keys, DO_NOT_CONVERT);
} else {
this->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::JSProxyOwnPropertyKeys(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 this->GetKeys_Internal(receiver, target, OWN_ONLY);
}
// 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|.)
}
}
this->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 this->AddKeysFromProxy(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 this->AddKeysFromProxy(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 this->AddKeysFromProxy(proxy, trap_result);
}
} // namespace internal
} // namespace v8
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_KEY_ACCUMULATOR_H_
#define V8_KEY_ACCUMULATOR_H_
#ifndef V8_KEYS_H_
#define V8_KEYS_H_
#include "src/isolate.h"
#include "src/objects.h"
......@@ -53,6 +53,16 @@ class KeyAccumulator final BASE_EMBEDDED {
int length() { return length_; }
Isolate* isolate() { return isolate_; }
void set_filter_proxy_keys(bool filter) { filter_proxy_keys_ = filter; }
PropertyFilter filter() { return filter_; }
Maybe<bool> GetKeys_Internal(Handle<JSReceiver> receiver,
Handle<JSReceiver> object,
KeyCollectionType type);
static Handle<FixedArray> GetEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object);
void CollectOwnElementKeys(Handle<JSObject> object);
void CollectOwnPropertyNames(Handle<JSObject> object);
private:
bool AddIntegerKey(uint32_t key);
......@@ -60,6 +70,12 @@ class KeyAccumulator final BASE_EMBEDDED {
bool AddSymbolKey(Handle<Object> array);
void SortCurrentElementsListRemoveDuplicates();
Maybe<bool> JSProxyOwnPropertyKeys(Handle<JSReceiver> receiver,
Handle<JSProxy> proxy);
Maybe<bool> GetKeysFromJSObject(Handle<JSReceiver> receiver,
Handle<JSObject> object,
KeyCollectionType type);
Isolate* isolate_;
KeyCollectionType type_;
PropertyFilter filter_;
......@@ -126,4 +142,4 @@ class FastKeyAccumulator {
} // namespace internal
} // namespace v8
#endif // V8_KEY_ACCUMULATOR_H_
#endif // V8_KEYS_H_
......@@ -7,6 +7,7 @@
#include "src/api.h"
#include "src/execution.h"
#include "src/isolate-inl.h"
#include "src/keys.h"
#include "src/string-builder.h"
namespace v8 {
......@@ -271,7 +272,8 @@ Handle<Object> CallSite::GetMethodName() {
if (!current->IsJSObject()) break;
Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
if (current_obj->IsAccessCheckNeeded()) break;
Handle<FixedArray> keys = JSObject::GetEnumPropertyKeys(current_obj);
Handle<FixedArray> keys =
KeyAccumulator::GetEnumPropertyKeys(isolate_, current_obj);
for (int i = 0; i < keys->length(); i++) {
HandleScope inner_scope(isolate_);
if (!keys->get(i)->IsName()) continue;
......
......@@ -763,17 +763,6 @@ MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
}
#define STACK_CHECK(result_value) \
do { \
StackLimitCheck stack_check(isolate); \
if (stack_check.HasOverflowed()) { \
isolate->Throw(*isolate->factory()->NewRangeError( \
MessageTemplate::kStackOverflow)); \
return result_value; \
} \
} while (false)
// static
MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
Handle<JSProxy> proxy,
......@@ -789,7 +778,7 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
}
DCHECK(!name->IsPrivate());
STACK_CHECK(MaybeHandle<Object>());
STACK_CHECK(isolate, MaybeHandle<Object>());
Handle<Name> trap_name = isolate->factory()->get_string();
// 1. Assert: IsPropertyKey(P) is true.
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
......@@ -1002,7 +991,7 @@ MaybeHandle<Object> JSProxy::GetPrototype(Handle<JSProxy> proxy) {
Isolate* isolate = proxy->GetIsolate();
Handle<String> trap_name = isolate->factory()->getPrototypeOf_string();
STACK_CHECK(MaybeHandle<Object>());
STACK_CHECK(isolate, MaybeHandle<Object>());
// 1. Let handler be the value of the [[ProxyHandler]] internal slot.
// 2. If handler is null, throw a TypeError exception.
......@@ -4849,7 +4838,7 @@ void JSProxy::Revoke(Handle<JSProxy> proxy) {
Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy,
Handle<Name> name) {
DCHECK(!name->IsPrivate());
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
// 1. (Assert)
// 2. Let handler be the value of the [[ProxyHandler]] internal slot of O.
Handle<Object> handler(proxy->handler(), isolate);
......@@ -4918,7 +4907,7 @@ Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name,
LanguageMode language_mode) {
DCHECK(!name->IsPrivate());
Isolate* isolate = proxy->GetIsolate();
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
Handle<String> trap_name = factory->set_string();
ShouldThrow should_throw =
......@@ -4989,7 +4978,7 @@ Maybe<bool> JSProxy::DeletePropertyOrElement(Handle<JSProxy> proxy,
ShouldThrow should_throw =
is_sloppy(language_mode) ? DONT_THROW : THROW_ON_ERROR;
Isolate* isolate = proxy->GetIsolate();
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
Handle<String> trap_name = factory->deleteProperty_string();
......@@ -6737,7 +6726,7 @@ Maybe<bool> JSProxy::DefineOwnProperty(Isolate* isolate, Handle<JSProxy> proxy,
Handle<Object> key,
PropertyDescriptor* desc,
ShouldThrow should_throw) {
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
if (key->IsSymbol() && Handle<Symbol>::cast(key)->IsPrivate()) {
return SetPrivateProperty(isolate, proxy, Handle<Symbol>::cast(key), desc,
should_throw);
......@@ -6958,7 +6947,7 @@ Maybe<bool> JSProxy::GetOwnPropertyDescriptor(Isolate* isolate,
Handle<Name> name,
PropertyDescriptor* desc) {
DCHECK(!name->IsPrivate());
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
Handle<String> trap_name =
isolate->factory()->getOwnPropertyDescriptor_string();
......@@ -7316,7 +7305,7 @@ Maybe<bool> JSReceiver::PreventExtensions(Handle<JSReceiver> object,
Maybe<bool> JSProxy::PreventExtensions(Handle<JSProxy> proxy,
ShouldThrow should_throw) {
Isolate* isolate = proxy->GetIsolate();
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
Handle<String> trap_name = factory->preventExtensions_string();
......@@ -7418,7 +7407,7 @@ Maybe<bool> JSReceiver::IsExtensible(Handle<JSReceiver> object) {
Maybe<bool> JSProxy::IsExtensible(Handle<JSProxy> proxy) {
Isolate* isolate = proxy->GetIsolate();
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
Factory* factory = isolate->factory();
Handle<String> trap_name = factory->isExtensible_string();
......@@ -7752,7 +7741,7 @@ MaybeHandle<JSObject> JSObjectWalkVisitor<ContextObject>::StructureWalk(
ONLY_WRITABLE | ONLY_ENUMERABLE | ONLY_CONFIGURABLE);
KeyAccumulator accumulator(isolate, OWN_ONLY, filter);
accumulator.NextPrototype();
copy->CollectOwnPropertyNames(&accumulator, filter);
accumulator.CollectOwnPropertyNames(copy);
Handle<FixedArray> names = accumulator.GetKeys();
for (int i = 0; i < names->length(); i++) {
DCHECK(names->get(i)->IsName());
......@@ -8059,13 +8048,6 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
}
static Handle<FixedArray> ReduceFixedArrayTo(
Handle<FixedArray> array, int length) {
DCHECK_LE(length, array->length());
if (array->length() == length) return array;
return array->GetIsolate()->factory()->CopyFixedArrayUpTo(array, length);
}
bool Map::OnlyHasSimpleProperties() {
// Wrapped string elements aren't explicitly stored in the elements backing
// store, but are loaded indirectly from the underlying string.
......@@ -8074,404 +8056,6 @@ bool Map::OnlyHasSimpleProperties() {
!has_hidden_prototype() && !is_dictionary_map();
}
// static
Handle<FixedArray> JSObject::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(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;
}
Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object) {
Isolate* isolate = object->GetIsolate();
if (object->HasFastProperties()) {
return GetFastEnumPropertyKeys(isolate, object);
} else if (object->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(object->global_dictionary());
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());
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;
}
}
enum IndexedOrNamed { kIndexed, kNamed };
// Returns |true| on success, |nothing| on exception.
template <class Callback, IndexedOrNamed type>
static Maybe<bool> GetKeysFromInterceptor(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSObject> object,
PropertyFilter filter,
KeyAccumulator* accumulator) {
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 ((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);
}
// Returns |true| on success, |false| if prototype walking should be stopped,
// |nothing| if an exception was thrown.
static Maybe<bool> GetKeysFromJSObject(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSObject> object,
PropertyFilter* filter,
KeyCollectionType type,
KeyAccumulator* accumulator) {
accumulator->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);
}
JSObject::CollectOwnElementKeys(object, accumulator, *filter);
// Add the element keys from the interceptor.
Maybe<bool> success =
GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback, kIndexed>(
isolate, receiver, object, *filter, accumulator);
MAYBE_RETURN(success, Nothing<bool>());
if (*filter == ENUMERABLE_STRINGS) {
Handle<FixedArray> enum_keys = JSObject::GetEnumPropertyKeys(object);
accumulator->AddKeys(enum_keys, DO_NOT_CONVERT);
} else {
object->CollectOwnPropertyNames(accumulator, *filter);
}
// Add the property keys from the interceptor.
success = GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback,
kNamed>(isolate, receiver, object, *filter,
accumulator);
MAYBE_RETURN(success, Nothing<bool>());
return Just(true);
}
// Helper function for JSReceiver::GetKeys() below. Can be called recursively.
// Returns |true| or |nothing|.
static Maybe<bool> GetKeys_Internal(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSReceiver> object,
KeyCollectionType type,
PropertyFilter filter,
KeyAccumulator* accumulator) {
// 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(JSProxy::OwnPropertyKeys(isolate, receiver,
Handle<JSProxy>::cast(object), filter,
accumulator),
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 = JSProxy::OwnPropertyKeys(isolate, receiver,
Handle<JSProxy>::cast(current), filter,
accumulator);
} else {
DCHECK(current->IsJSObject());
result = GetKeysFromJSObject(isolate, receiver,
Handle<JSObject>::cast(current), &filter,
type, accumulator);
}
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 handlede in GgetKeysFromJSObject.
if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
return Nothing<bool>();
}
}
return Just(true);
}
// ES6 9.5.12
// Returns |true| on success, |nothing| in case of exception.
// static
Maybe<bool> JSProxy::OwnPropertyKeys(Isolate* isolate,
Handle<JSReceiver> receiver,
Handle<JSProxy> proxy,
PropertyFilter filter,
KeyAccumulator* accumulator) {
STACK_CHECK(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 GetKeys_Internal(isolate, receiver, target, OWN_ONLY, filter,
accumulator);
}
// 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|.)
}
}
accumulator->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 accumulator->AddKeysFromProxy(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 accumulator->AddKeysFromProxy(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 accumulator->AddKeysFromProxy(proxy, trap_result);
}
MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
KeyCollectionType type,
PropertyFilter filter,
......@@ -8481,9 +8065,8 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
Isolate* isolate = object->GetIsolate();
KeyAccumulator accumulator(isolate, type, filter);
accumulator.set_filter_proxy_keys(filter_proxy_keys);
MAYBE_RETURN(
GetKeys_Internal(isolate, object, object, type, filter, &accumulator),
MaybeHandle<FixedArray>());
MAYBE_RETURN(accumulator.GetKeys_Internal(object, object, type),
MaybeHandle<FixedArray>());
Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion);
DCHECK(ContainsOnlyValidKeys(keys));
return keys;
......@@ -8580,8 +8163,7 @@ MaybeHandle<FixedArray> GetOwnValuesOrEntries(Isolate* isolate,
PropertyFilter key_filter =
static_cast<PropertyFilter>(filter & ~ONLY_ENUMERABLE);
KeyAccumulator accumulator(isolate, OWN_ONLY, key_filter);
MAYBE_RETURN(GetKeys_Internal(isolate, object, object, OWN_ONLY, key_filter,
&accumulator),
MAYBE_RETURN(accumulator.GetKeys_Internal(object, object, OWN_ONLY),
MaybeHandle<FixedArray>());
Handle<FixedArray> keys = accumulator.GetKeys(CONVERT_TO_STRING);
DCHECK(ContainsOnlyValidKeys(keys));
......@@ -14658,7 +14240,7 @@ Maybe<bool> JSProxy::SetPrototype(Handle<JSProxy> proxy, Handle<Object> value,
bool from_javascript,
ShouldThrow should_throw) {
Isolate* isolate = proxy->GetIsolate();
STACK_CHECK(Nothing<bool>());
STACK_CHECK(isolate, Nothing<bool>());
Handle<Name> trap_name = isolate->factory()->setPrototypeOf_string();
// 1. Assert: Either Type(V) is Object or Type(V) is Null.
DCHECK(value->IsJSReceiver() || value->IsNull());
......@@ -15509,31 +15091,6 @@ void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
}
}
void JSObject::CollectOwnPropertyNames(KeyAccumulator* keys,
PropertyFilter filter) {
if (HasFastProperties()) {
int real_size = map()->NumberOfOwnDescriptors();
Handle<DescriptorArray> descs(map()->instance_descriptors());
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;
keys->AddKey(key, DO_NOT_CONVERT);
}
} else if (IsJSGlobalObject()) {
GlobalDictionary::CollectKeysTo(handle(global_dictionary()), keys, filter);
} else {
NameDictionary::CollectKeysTo(handle(property_dictionary()), keys, filter);
}
}
bool JSObject::WasConstructedFromApiFunction() {
auto instance_type = map()->instance_type();
bool is_api_object = instance_type == JS_API_OBJECT_TYPE ||
......@@ -15553,15 +15110,6 @@ bool JSObject::WasConstructedFromApiFunction() {
return is_api_object;
}
void JSObject::CollectOwnElementKeys(Handle<JSObject> object,
KeyAccumulator* keys,
PropertyFilter filter) {
if (filter & SKIP_STRINGS) return;
ElementsAccessor* accessor = object->GetElementsAccessor();
accessor->CollectElementIndices(object, keys, kMaxUInt32, filter, 0);
}
MaybeHandle<String> Object::ObjectProtoToString(Isolate* isolate,
Handle<Object> object) {
if (object->IsUndefined()) return isolate->factory()->undefined_to_string();
......@@ -16414,6 +15962,30 @@ template int HashTable<SeededNumberDictionary, SeededNumberDictionaryShape,
template int NameDictionaryBase<NameDictionary, NameDictionaryShape>::FindEntry(
Handle<Name>);
template int Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::
NumberOfElementsFilterAttributes(PropertyFilter filter);
template int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::
NumberOfElementsFilterAttributes(PropertyFilter filter);
template void Dictionary<GlobalDictionary, GlobalDictionaryShape,
Handle<Name>>::CopyEnumKeysTo(FixedArray* storage);
template void Dictionary<NameDictionary, NameDictionaryShape,
Handle<Name>>::CopyEnumKeysTo(FixedArray* storage);
template void
Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::
CollectKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape,
Handle<Name>>>
dictionary,
KeyAccumulator* keys, PropertyFilter filter);
template void
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CollectKeysTo(
Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>>
dictionary,
KeyAccumulator* keys, PropertyFilter filter);
Handle<Object> JSObject::PrepareSlowElementsForSort(
Handle<JSObject> object, uint32_t limit) {
......
......@@ -2270,18 +2270,6 @@ class JSObject: public JSReceiver {
inline void SetInternalField(int index, Smi* value);
bool WasConstructedFromApiFunction();
void CollectOwnPropertyNames(KeyAccumulator* keys,
PropertyFilter filter = ALL_PROPERTIES);
static void CollectOwnElementKeys(Handle<JSObject> object,
KeyAccumulator* keys,
PropertyFilter filter);
static Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object);
static Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object);
// Returns a new map with all transitions dropped from the object's current
// map and the ElementsKind set.
static Handle<Map> GetElementsTransitionMap(Handle<JSObject> object,
......
......@@ -213,7 +213,7 @@ RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
}
accumulator.NextPrototype();
Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
JSObject::CollectOwnElementKeys(current, &accumulator, ALL_PROPERTIES);
accumulator.CollectOwnElementKeys(current);
}
// Erase any keys >= length.
Handle<FixedArray> keys = accumulator.GetKeys(KEEP_NUMBERS);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment