Chromium Code Reviews| Index: src/keys.cc |
| diff --git a/src/keys.cc b/src/keys.cc |
| index 4cbd3d895edaae7b997e01f4661fab1f6a24e3f4..dc0450202940e885605a2f5c7f3b26f75584834b 100644 |
| --- a/src/keys.cc |
| +++ b/src/keys.cc |
| @@ -34,12 +34,20 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) { |
| MaybeHandle<FixedArray> KeyAccumulator::GetKeys( |
| Handle<JSReceiver> object, KeyCollectionMode mode, PropertyFilter filter, |
| - GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in) { |
| + GetKeysConversion keys_conversion, bool filter_proxy_keys, bool is_for_in, |
| + JSReceiver* first_non_empty_prototype, |
| + JSReceiver* last_non_empty_prototype) { |
| 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); |
| + if (first_non_empty_prototype != nullptr) { |
| + accumulator.set_first_non_empty_prototype(first_non_empty_prototype); |
| + } |
| + if (first_non_empty_prototype != nullptr) { |
| + accumulator.set_last_non_empty_prototype(last_non_empty_prototype); |
| + } |
| MAYBE_RETURN(accumulator.CollectKeys(object, object), |
| MaybeHandle<FixedArray>()); |
| Handle<FixedArray> keys = accumulator.GetKeys(keys_conversion); |
| @@ -157,6 +165,9 @@ Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver, |
| PrototypeIterator::WhereToEnd end = mode_ == KeyCollectionMode::kOwnOnly |
| ? PrototypeIterator::END_AT_NON_HIDDEN |
| : PrototypeIterator::END_AT_NULL; |
| + if (!first_non_empty_prototype_.is_null()) { |
| + object = first_non_empty_prototype_; |
| + } |
| for (PrototypeIterator iter(isolate_, object, |
| PrototypeIterator::START_AT_RECEIVER, end); |
| !iter.IsAtEnd();) { |
| @@ -176,6 +187,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 +228,27 @@ 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* first_prototype; |
| + JSReceiver* last_prototype; |
| for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); |
| iter.Advance()) { |
| JSReceiver* current = iter.GetCurrent<JSReceiver>(); |
| - if (CheckAndInitalizeSimpleEnumCache(current)) continue; |
| - has_empty_prototype_ = false; |
| - first_non_empty_prototype = current; |
| - // TODO(cbruni): use the first non-empty prototype. |
| - USE(first_non_empty_prototype); |
| - return; |
| + bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current); |
| + if (has_no_properties) continue; |
| + last_prototype = current; |
| + if (has_empty_prototype_) { |
| + has_empty_prototype_ = false; |
| + first_prototype = current; |
| + } |
| + } |
| + if (has_empty_prototype_) { |
| + is_receiver_simple_enum_ = |
| + receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && |
| + !JSObject::cast(*receiver_)->HasEnumerableElements(); |
| + } else { |
| + first_non_empty_prototype_ = handle(first_prototype, isolate_); |
| + last_non_empty_prototype_ = handle(last_prototype, isolate_); |
| } |
| - DCHECK(has_empty_prototype_); |
| - is_receiver_simple_enum_ = |
| - receiver_->map()->EnumLength() != kInvalidEnumCacheSentinel && |
| - !JSObject::cast(*receiver_)->HasEnumerableElements(); |
| } |
| namespace { |
| @@ -364,7 +385,7 @@ bool OnlyHasSimpleProperties(Map* map) { |
| MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(GetKeysConversion convert) { |
| Handle<FixedArray> keys; |
| - if (GetKeysFast(convert).ToHandle(&keys)) { |
| + if (filter_ == ENUMERABLE_STRINGS && GetKeysFast(convert).ToHandle(&keys)) { |
| return keys; |
| } |
| return GetKeysSlow(convert); |
| @@ -408,9 +429,10 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( |
| MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( |
| GetKeysConversion convert) { |
| - return KeyAccumulator::GetKeys(receiver_, mode_, filter_, |
| - GetKeysConversion::kKeepNumbers, |
| - filter_proxy_keys_, is_for_in_); |
| + return KeyAccumulator::GetKeys( |
| + receiver_, mode_, filter_, GetKeysConversion::kKeepNumbers, |
| + filter_proxy_keys_, is_for_in_, *first_non_empty_prototype_, |
|
Jakob Kummerow
2016/06/06 12:54:40
any particular reason for the deref/rehandlify dan
|
| + *last_non_empty_prototype_); |
| } |
| namespace { |
| @@ -419,7 +441,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 +484,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 +546,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); |
| } |