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

[keys] keep track of the last non-empty prototype

Without the boundary prototypes we have to keep track of all shadowing properties
throughout the complete prototype chain. This contradicts the finding that most
objects have a rather large number of non-enumerable properties on the prototype
chain.

BUG=v8:705, v8:4905, v8:4706

Review-Url: https://codereview.chromium.org/2038043002
Cr-Commit-Position: refs/heads/master@{#36776}
parent 85b8c2dc
......@@ -31,20 +31,16 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
}
} // namespace
MaybeHandle<FixedArray> KeyAccumulator::GetKeys(
Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) {
USE(ContainsOnlyValidKeys);
Isolate* isolate = object->GetIsolate();
KeyAccumulator accumulator(isolate, mode, filter);
accumulator.set_filter_proxy_keys(filter_proxy_keys);
accumulator.set_is_for_in(is_for_in);
MAYBE_RETURN(accumulator.CollectKeys(object, object),
MaybeHandle<FixedArray>());
Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion);
DCHECK(ContainsOnlyValidKeys(keys));
return keys;
return accumulator.GetKeys(keys_conversion);
}
Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
......@@ -55,7 +51,11 @@ Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
keys_->map() == isolate_->heap()->fixed_array_map()) {
return Handle<FixedArray>::cast(keys_);
}
return OrderedHashSet::ConvertToKeysArray(keys(), convert);
USE(ContainsOnlyValidKeys);
Handle<FixedArray> result =
OrderedHashSet::ConvertToKeysArray(keys(), convert);
DCHECK(ContainsOnlyValidKeys(result));
return result;
}
void KeyAccumulator::AddKey(Object* key, AddKeyConversion convert) {
......@@ -176,6 +176,10 @@ Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
if (!iter.AdvanceFollowingProxiesIgnoringAccessChecks()) {
return Nothing<bool>();
}
if (!last_non_empty_prototype_.is_null() &&
*last_non_empty_prototype_ == *current) {
break;
}
}
return Just(true);
}
......@@ -213,21 +217,22 @@ void FastKeyAccumulator::Prepare() {
// Fully walk the prototype chain and find the last prototype with keys.
is_receiver_simple_enum_ = false;
has_empty_prototype_ = true;
JSReceiver* first_non_empty_prototype;
JSReceiver* last_prototype = nullptr;
for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
iter.Advance()) {
JSReceiver* current = iter.GetCurrent<JSReceiver>();
if (CheckAndInitalizeSimpleEnumCache(current)) continue;
bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current);
if (has_no_properties) continue;
last_prototype = current;
has_empty_prototype_ = false;
first_non_empty_prototype = current;
// TODO(cbruni): use the first non-empty prototype.
USE(first_non_empty_prototype);
return;
}
DCHECK(has_empty_prototype_);
is_receiver_simple_enum_ =
receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
!JSObject::cast(*receiver_)->HasEnumerableElements();
if (has_empty_prototype_) {
is_receiver_simple_enum_ =
receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel &&
!JSObject::cast(*receiver_)->HasEnumerableElements();
} else if (last_prototype != nullptr) {
last_non_empty_prototype_ = handle(last_prototype, isolate_);
}
}
namespace {
......@@ -362,16 +367,18 @@ bool OnlyHasSimpleProperties(Map* map) {
} // namespace
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) {
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(
GetKeysConversion keys_conversion) {
Handle<FixedArray> keys;
if (GetKeysFast(convert).ToHandle(&keys)) {
if (filter_ == ENUMERABLE_STRINGS &&
GetKeysFast(keys_conversion).ToHandle(&keys)) {
return keys;
}
return GetKeysSlow(convert);
return GetKeysSlow(keys_conversion);
}
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
GetKeysConversion convert) {
GetKeysConversion keys_conversion) {
bool own_only = has_empty_prototype_ || mode_ == KeyCollectionMode::kOwnOnly;
Map* map = receiver_->map();
if (!own_only || !OnlyHasSimpleProperties(map)) {
......@@ -384,7 +391,7 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
// Do not try to use the enum-cache for dict-mode objects.
if (map->is_dictionary_map()) {
return GetOwnKeysWithElements<false>(isolate_, object, convert);
return GetOwnKeysWithElements<false>(isolate_, object, keys_conversion);
}
int enum_length = receiver_->map()->EnumLength();
if (enum_length == kInvalidEnumCacheSentinel) {
......@@ -403,14 +410,19 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
}
// The properties-only case failed because there were probably elements on the
// receiver.
return GetOwnKeysWithElements<true>(isolate_, object, convert);
return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion);
}
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
GetKeysConversion convert) {
return KeyAccumulator::GetKeys(receiver_, mode_, filter_,
GetKeysConversion::kKeepNumbers,
filter_proxy_keys_, is_for_in_);
GetKeysConversion keys_conversion) {
KeyAccumulator accumulator(isolate_, mode_, filter_);
accumulator.set_filter_proxy_keys(filter_proxy_keys_);
accumulator.set_is_for_in(is_for_in_);
accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
MaybeHandle<FixedArray>());
return accumulator.GetKeys(keys_conversion);
}
namespace {
......@@ -419,7 +431,7 @@ enum IndexedOrNamed { kIndexed, kNamed };
// Returns |true| on success, |nothing| on exception.
template <class Callback, IndexedOrNamed type>
Maybe<bool> GetKeysFromInterceptor(Handle<JSReceiver> receiver,
Maybe<bool> CollectInterceptorKeys(Handle<JSReceiver> receiver,
Handle<JSObject> object,
KeyAccumulator* accumulator) {
Isolate* isolate = accumulator->isolate();
......@@ -462,7 +474,7 @@ Maybe<bool> KeyAccumulator::CollectOwnElementIndices(
ElementsAccessor* accessor = object->GetElementsAccessor();
accessor->CollectElementIndices(object, this);
return GetKeysFromInterceptor<v8::IndexedPropertyEnumeratorCallback,
return CollectInterceptorKeys<v8::IndexedPropertyEnumeratorCallback,
kIndexed>(receiver, object, this);
}
......@@ -524,7 +536,7 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
}
}
// Add the property keys from the interceptor.
return GetKeysFromInterceptor<v8::GenericNamedPropertyEnumeratorCallback,
return CollectInterceptorKeys<v8::GenericNamedPropertyEnumeratorCallback,
kNamed>(receiver, object, this);
}
......
......@@ -40,6 +40,7 @@ class KeyAccumulator final BASE_EMBEDDED {
Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter,
GetKeysConversion keys_conversion = GetKeysConversion::kKeepNumbers,
bool filter_proxy_keys = true, bool is_for_in = false);
Handle<FixedArray> GetKeys(
GetKeysConversion convert = GetKeysConversion::kKeepNumbers);
Maybe<bool> CollectKeys(Handle<JSReceiver> receiver,
......@@ -64,6 +65,9 @@ class KeyAccumulator final BASE_EMBEDDED {
void set_filter_proxy_keys(bool filter) { filter_proxy_keys_ = filter; }
void set_is_for_in(bool value) { is_for_in_ = value; }
void set_skip_indices(bool value) { skip_indices_ = value; }
void set_last_non_empty_prototype(Handle<JSReceiver> object) {
last_non_empty_prototype_ = object;
}
private:
Maybe<bool> CollectOwnKeys(Handle<JSReceiver> receiver,
......@@ -82,6 +86,7 @@ class KeyAccumulator final BASE_EMBEDDED {
// keys_ is either an Handle<OrderedHashSet> or in the case of own JSProxy
// keys a Handle<FixedArray>.
Handle<FixedArray> keys_;
Handle<JSReceiver> last_non_empty_prototype_;
KeyCollectionMode mode_;
PropertyFilter filter_;
bool filter_proxy_keys_ = true;
......@@ -117,6 +122,7 @@ class FastKeyAccumulator {
Isolate* isolate_;
Handle<JSReceiver> receiver_;
Handle<JSReceiver> last_non_empty_prototype_;
KeyCollectionMode mode_;
PropertyFilter filter_;
bool filter_proxy_keys_ = true;
......
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