[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}
This commit is contained in:
parent
2af52484cd
commit
6c7d51c296
50
src/keys.cc
50
src/keys.cc
@ -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_;
|
||||
|
Loading…
Reference in New Issue
Block a user