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); |
} |