Commit 6c7d51c2 authored by cbruni's avatar cbruni Committed by Commit bot

[keys] Make for-in great again.

This CL fixes the check for empty elements in keys.cc. Previously we would
accidentally bail out of the fast path because the check would always fail.
As a consequence for-in loops that would initialize the enum-cache of an object
with own-only fast properties would never be optimized properly.

Review-Url: https://codereview.chromium.org/2638323002
Cr-Commit-Position: refs/heads/master@{#42454}
parent 2af52484
...@@ -227,7 +227,7 @@ void TrySettingEmptyEnumCache(JSReceiver* object) { ...@@ -227,7 +227,7 @@ void TrySettingEmptyEnumCache(JSReceiver* object) {
map->SetEnumLength(0); map->SetEnumLength(0);
} }
bool CheckAndInitalizeSimpleEnumCache(JSReceiver* object) { bool CheckAndInitalizeEmptyEnumCache(JSReceiver* object) {
if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) { if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
TrySettingEmptyEnumCache(object); TrySettingEmptyEnumCache(object);
} }
...@@ -248,7 +248,7 @@ void FastKeyAccumulator::Prepare() { ...@@ -248,7 +248,7 @@ void FastKeyAccumulator::Prepare() {
for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd(); for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
iter.Advance()) { iter.Advance()) {
JSReceiver* current = iter.GetCurrent<JSReceiver>(); JSReceiver* current = iter.GetCurrent<JSReceiver>();
bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current); bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
if (has_no_properties) continue; if (has_no_properties) continue;
last_prototype = current; last_prototype = current;
has_empty_prototype_ = false; has_empty_prototype_ = false;
...@@ -271,6 +271,8 @@ static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate, ...@@ -271,6 +271,8 @@ static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
return isolate->factory()->CopyFixedArrayUpTo(array, length); return isolate->factory()->CopyFixedArrayUpTo(array, length);
} }
// Initializes and directly returns the enume cache. Users of this function
// have to make sure to never directly leak the enum cache.
Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate, Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object) { Handle<JSObject> object) {
Handle<Map> map(object->map()); Handle<Map> map(object->map());
...@@ -370,25 +372,6 @@ MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate, ...@@ -370,25 +372,6 @@ MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
return result; return result;
} }
MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache(
Isolate* isolate, Handle<JSObject> object) {
// Uninitalized enum cache
Map* map = object->map();
if (object->elements() != isolate->heap()->empty_fixed_array() ||
object->elements() != isolate->heap()->empty_slow_element_dictionary()) {
// Assume that there are elements.
return MaybeHandle<FixedArray>();
}
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
if (number_of_own_descriptors == 0) {
map->SetEnumLength(0);
return isolate->factory()->empty_fixed_array();
}
// We have no elements but possibly enumerable property keys, hence we can
// directly initialize the enum cache.
return GetFastEnumPropertyKeys(isolate, object);
}
bool OnlyHasSimpleProperties(Map* map) { bool OnlyHasSimpleProperties(Map* map) {
return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER; return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER;
} }
...@@ -428,8 +411,7 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( ...@@ -428,8 +411,7 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
if (enum_length == kInvalidEnumCacheSentinel) { if (enum_length == kInvalidEnumCacheSentinel) {
Handle<FixedArray> keys; Handle<FixedArray> keys;
// Try initializing the enum cache and return own properties. // Try initializing the enum cache and return own properties.
if (GetOwnKeysWithUninitializedEnumCache(isolate_, object) if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) {
.ToHandle(&keys)) {
if (FLAG_trace_for_in_enumerate) { if (FLAG_trace_for_in_enumerate) {
PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n", PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n",
keys->length()); keys->length());
...@@ -444,6 +426,28 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast( ...@@ -444,6 +426,28 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion); return GetOwnKeysWithElements<true>(isolate_, object, keys_conversion);
} }
MaybeHandle<FixedArray>
FastKeyAccumulator::GetOwnKeysWithUninitializedEnumCache() {
Handle<JSObject> object = Handle<JSObject>::cast(receiver_);
// Uninitalized enum cache
Map* map = object->map();
if (object->elements()->length() != 0) {
// Assume that there are elements.
return MaybeHandle<FixedArray>();
}
int number_of_own_descriptors = map->NumberOfOwnDescriptors();
if (number_of_own_descriptors == 0) {
map->SetEnumLength(0);
return isolate_->factory()->empty_fixed_array();
}
// We have no elements but possibly enumerable property keys, hence we can
// directly initialize the enum cache.
Handle<FixedArray> keys = GetFastEnumPropertyKeys(isolate_, object);
if (is_for_in_) return keys;
// Do not leak the enum cache as it might end up as an elements backing store.
return isolate_->factory()->CopyFixedArray(keys);
}
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow( MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
GetKeysConversion keys_conversion) { GetKeysConversion keys_conversion) {
KeyAccumulator accumulator(isolate_, mode_, filter_); KeyAccumulator accumulator(isolate_, mode_, filter_);
......
...@@ -53,9 +53,10 @@ class KeyAccumulator final BASE_EMBEDDED { ...@@ -53,9 +53,10 @@ class KeyAccumulator final BASE_EMBEDDED {
Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver, Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
Handle<JSObject> object); Handle<JSObject> object);
// Might return directly the object's enum_cache, copy the result before using
// as an elements backing store for a JSObject.
static Handle<FixedArray> GetOwnEnumPropertyKeys(Isolate* isolate, static Handle<FixedArray> GetOwnEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object); Handle<JSObject> object);
void AddKey(Object* key, AddKeyConversion convert = DO_NOT_CONVERT); void AddKey(Object* key, AddKeyConversion convert = DO_NOT_CONVERT);
void AddKey(Handle<Object> key, AddKeyConversion convert = DO_NOT_CONVERT); void AddKey(Handle<Object> key, AddKeyConversion convert = DO_NOT_CONVERT);
void AddKeys(Handle<FixedArray> array, AddKeyConversion convert); void AddKeys(Handle<FixedArray> array, AddKeyConversion convert);
...@@ -140,6 +141,8 @@ class FastKeyAccumulator { ...@@ -140,6 +141,8 @@ class FastKeyAccumulator {
MaybeHandle<FixedArray> GetKeysFast(GetKeysConversion convert); MaybeHandle<FixedArray> GetKeysFast(GetKeysConversion convert);
MaybeHandle<FixedArray> GetKeysSlow(GetKeysConversion convert); MaybeHandle<FixedArray> GetKeysSlow(GetKeysConversion convert);
MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache();
Isolate* isolate_; Isolate* isolate_;
Handle<JSReceiver> receiver_; Handle<JSReceiver> receiver_;
Handle<JSReceiver> last_non_empty_prototype_; Handle<JSReceiver> last_non_empty_prototype_;
......
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