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) {
map->SetEnumLength(0);
}
bool CheckAndInitalizeSimpleEnumCache(JSReceiver* object) {
bool CheckAndInitalizeEmptyEnumCache(JSReceiver* object) {
if (object->map()->EnumLength() == kInvalidEnumCacheSentinel) {
TrySettingEmptyEnumCache(object);
}
......@@ -248,7 +248,7 @@ void FastKeyAccumulator::Prepare() {
for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
iter.Advance()) {
JSReceiver* current = iter.GetCurrent<JSReceiver>();
bool has_no_properties = CheckAndInitalizeSimpleEnumCache(current);
bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
if (has_no_properties) continue;
last_prototype = current;
has_empty_prototype_ = false;
......@@ -271,6 +271,8 @@ static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
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<JSObject> object) {
Handle<Map> map(object->map());
......@@ -370,25 +372,6 @@ MaybeHandle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
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) {
return map->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER;
}
......@@ -428,8 +411,7 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
if (enum_length == kInvalidEnumCacheSentinel) {
Handle<FixedArray> keys;
// Try initializing the enum cache and return own properties.
if (GetOwnKeysWithUninitializedEnumCache(isolate_, object)
.ToHandle(&keys)) {
if (GetOwnKeysWithUninitializedEnumCache().ToHandle(&keys)) {
if (FLAG_trace_for_in_enumerate) {
PrintF("| strings=%d symbols=0 elements=0 || prototypes>=1 ||\n",
keys->length());
......@@ -444,6 +426,28 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysFast(
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(
GetKeysConversion keys_conversion) {
KeyAccumulator accumulator(isolate_, mode_, filter_);
......
......@@ -53,9 +53,10 @@ class KeyAccumulator final BASE_EMBEDDED {
Handle<AccessCheckInfo> access_check_info, Handle<JSReceiver> receiver,
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,
Handle<JSObject> object);
void AddKey(Object* key, AddKeyConversion convert = DO_NOT_CONVERT);
void AddKey(Handle<Object> key, AddKeyConversion convert = DO_NOT_CONVERT);
void AddKeys(Handle<FixedArray> array, AddKeyConversion convert);
......@@ -140,6 +141,8 @@ class FastKeyAccumulator {
MaybeHandle<FixedArray> GetKeysFast(GetKeysConversion convert);
MaybeHandle<FixedArray> GetKeysSlow(GetKeysConversion convert);
MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache();
Isolate* isolate_;
Handle<JSReceiver> receiver_;
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