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

[keys] support shadowing keys in the KeyAccumulator

This cl fixes the long-standing bug for for-in with shadowing properties.

BUG=v8:705

Review-Url: https://codereview.chromium.org/2081733002
Cr-Commit-Position: refs/heads/master@{#37333}
parent 04b655c6
...@@ -1323,10 +1323,14 @@ class DictionaryElementsAccessor ...@@ -1323,10 +1323,14 @@ class DictionaryElementsAccessor
int insertion_index = 0; int insertion_index = 0;
PropertyFilter filter = keys->filter(); PropertyFilter filter = keys->filter();
for (int i = 0; i < capacity; i++) { for (int i = 0; i < capacity; i++) {
uint32_t key = GetKeyForEntryImpl(isolate, dictionary, i, filter); Object* raw_key = dictionary->KeyAt(i);
if (key == kMaxUInt32) continue; if (!dictionary->IsKey(isolate, raw_key)) continue;
Handle<Object> key_handle = isolate->factory()->NewNumberFromUint(key); uint32_t key = FilterKey(dictionary, i, raw_key, filter);
elements->set(insertion_index, *key_handle); if (key == kMaxUInt32) {
keys->AddShadowKey(raw_key);
continue;
}
elements->set(insertion_index, raw_key);
insertion_index++; insertion_index++;
} }
SortIndices(elements, insertion_index); SortIndices(elements, insertion_index);
......
...@@ -69,6 +69,7 @@ void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) { ...@@ -69,6 +69,7 @@ void KeyAccumulator::AddKey(Handle<Object> key, AddKeyConversion convert) {
} else if (filter_ & SKIP_STRINGS) { } else if (filter_ & SKIP_STRINGS) {
return; return;
} }
if (IsShadowed(key)) return;
if (keys_.is_null()) { if (keys_.is_null()) {
keys_ = OrderedHashSet::Allocate(isolate_, 16); keys_ = OrderedHashSet::Allocate(isolate_, 16);
} }
...@@ -96,13 +97,15 @@ void KeyAccumulator::AddKeys(Handle<JSObject> array_like, ...@@ -96,13 +97,15 @@ void KeyAccumulator::AddKeys(Handle<JSObject> array_like,
accessor->AddElementsToKeyAccumulator(array_like, this, convert); accessor->AddElementsToKeyAccumulator(array_like, this, convert);
} }
MaybeHandle<FixedArray> FilterProxyKeys(Isolate* isolate, Handle<JSProxy> owner, MaybeHandle<FixedArray> FilterProxyKeys(KeyAccumulator* accumulator,
Handle<JSProxy> owner,
Handle<FixedArray> keys, Handle<FixedArray> keys,
PropertyFilter filter) { PropertyFilter filter) {
if (filter == ALL_PROPERTIES) { if (filter == ALL_PROPERTIES) {
// Nothing to do. // Nothing to do.
return keys; return keys;
} }
Isolate* isolate = accumulator->isolate();
int store_position = 0; int store_position = 0;
for (int i = 0; i < keys->length(); ++i) { for (int i = 0; i < keys->length(); ++i) {
Handle<Name> key(Name::cast(keys->get(i)), isolate); Handle<Name> key(Name::cast(keys->get(i)), isolate);
...@@ -112,7 +115,11 @@ MaybeHandle<FixedArray> FilterProxyKeys(Isolate* isolate, Handle<JSProxy> owner, ...@@ -112,7 +115,11 @@ MaybeHandle<FixedArray> FilterProxyKeys(Isolate* isolate, Handle<JSProxy> owner,
Maybe<bool> found = Maybe<bool> found =
JSProxy::GetOwnPropertyDescriptor(isolate, owner, key, &desc); JSProxy::GetOwnPropertyDescriptor(isolate, owner, key, &desc);
MAYBE_RETURN(found, MaybeHandle<FixedArray>()); MAYBE_RETURN(found, MaybeHandle<FixedArray>());
if (!found.FromJust() || !desc.enumerable()) continue; // Skip this key. if (!found.FromJust()) continue;
if (!desc.enumerable()) {
accumulator->AddShadowKey(key);
continue;
}
} }
// Keep this key. // Keep this key.
if (store_position != i) { if (store_position != i) {
...@@ -131,7 +138,7 @@ Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy, ...@@ -131,7 +138,7 @@ Maybe<bool> KeyAccumulator::AddKeysFromJSProxy(Handle<JSProxy> proxy,
if (filter_proxy_keys_) { if (filter_proxy_keys_) {
DCHECK(!is_for_in_); DCHECK(!is_for_in_);
ASSIGN_RETURN_ON_EXCEPTION_VALUE( ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, keys, FilterProxyKeys(isolate_, proxy, keys, filter_), isolate_, keys, FilterProxyKeys(this, proxy, keys, filter_),
Nothing<bool>()); Nothing<bool>());
} }
if (mode_ == KeyCollectionMode::kOwnOnly && !is_for_in_) { if (mode_ == KeyCollectionMode::kOwnOnly && !is_for_in_) {
...@@ -183,6 +190,23 @@ Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver, ...@@ -183,6 +190,23 @@ Maybe<bool> KeyAccumulator::CollectKeys(Handle<JSReceiver> receiver,
return Just(true); return Just(true);
} }
bool KeyAccumulator::IsShadowed(Handle<Object> key) {
if (shadowed_keys_.is_null()) return false;
return shadowed_keys_->Has(isolate_, key);
}
void KeyAccumulator::AddShadowKey(Object* key) {
if (mode_ == KeyCollectionMode::kOwnOnly) return;
AddShadowKey(handle(key, isolate_));
}
void KeyAccumulator::AddShadowKey(Handle<Object> key) {
if (mode_ == KeyCollectionMode::kOwnOnly) return;
if (shadowed_keys_.is_null()) {
shadowed_keys_ = ObjectHashSet::New(isolate_, 16);
}
shadowed_keys_ = ObjectHashSet::Add(shadowed_keys_, key);
}
namespace { namespace {
void TrySettingEmptyEnumCache(JSReceiver* object) { void TrySettingEmptyEnumCache(JSReceiver* object) {
...@@ -329,7 +353,7 @@ Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate, ...@@ -329,7 +353,7 @@ Handle<FixedArray> GetOwnKeysWithElements(Isolate* isolate,
keys = GetFastEnumPropertyKeys(isolate, object); keys = GetFastEnumPropertyKeys(isolate, object);
} else { } else {
// TODO(cbruni): preallocate big enough array to also hold elements. // TODO(cbruni): preallocate big enough array to also hold elements.
keys = KeyAccumulator::GetEnumPropertyKeys(isolate, object); keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate, object);
} }
Handle<FixedArray> result = Handle<FixedArray> result =
accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE); accessor->PrependElementIndices(object, keys, convert, ONLY_ENUMERABLE);
...@@ -495,33 +519,87 @@ int CollectOwnPropertyNamesInternal(Handle<JSObject> object, ...@@ -495,33 +519,87 @@ int CollectOwnPropertyNamesInternal(Handle<JSObject> object,
Handle<DescriptorArray> descs, Handle<DescriptorArray> descs,
int start_index, int limit) { int start_index, int limit) {
int first_skipped = -1; int first_skipped = -1;
PropertyFilter filter = keys->filter();
KeyCollectionMode mode = keys->mode();
for (int i = start_index; i < limit; i++) { for (int i = start_index; i < limit; i++) {
bool is_shadowing_key = false;
PropertyDetails details = descs->GetDetails(i); PropertyDetails details = descs->GetDetails(i);
if ((details.attributes() & keys->filter()) != 0) continue;
if (keys->filter() & ONLY_ALL_CAN_READ) { if ((details.attributes() & filter) != 0) {
if (mode == KeyCollectionMode::kIncludePrototypes) {
is_shadowing_key = true;
} else {
continue;
}
}
if (filter & ONLY_ALL_CAN_READ) {
if (details.kind() != kAccessor) continue; if (details.kind() != kAccessor) continue;
Object* accessors = descs->GetValue(i); Object* accessors = descs->GetValue(i);
if (!accessors->IsAccessorInfo()) continue; if (!accessors->IsAccessorInfo()) continue;
if (!AccessorInfo::cast(accessors)->all_can_read()) continue; if (!AccessorInfo::cast(accessors)->all_can_read()) continue;
} }
Name* key = descs->GetKey(i); Name* key = descs->GetKey(i);
if (skip_symbols == key->IsSymbol()) { if (skip_symbols == key->IsSymbol()) {
if (first_skipped == -1) first_skipped = i; if (first_skipped == -1) first_skipped = i;
continue; continue;
} }
if (key->FilterKey(keys->filter())) continue; if (key->FilterKey(keys->filter())) continue;
keys->AddKey(key, DO_NOT_CONVERT);
if (is_shadowing_key) {
keys->AddShadowKey(key);
} else {
keys->AddKey(key, DO_NOT_CONVERT);
}
} }
return first_skipped; return first_skipped;
} }
template <class T>
Handle<FixedArray> GetOwnEnumPropertyDictionaryKeys(Isolate* isolate,
KeyCollectionMode mode,
KeyAccumulator* accumulator,
Handle<JSObject> object,
T* raw_dictionary) {
Handle<T> dictionary(raw_dictionary, isolate);
int length = dictionary->NumberOfEnumElements();
if (length == 0) {
return isolate->factory()->empty_fixed_array();
}
Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
T::CopyEnumKeysTo(dictionary, storage, mode, accumulator);
return storage;
}
} // namespace } // namespace
Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
Handle<JSObject> object) { Handle<JSObject> object) {
if (filter_ == ENUMERABLE_STRINGS) { if (filter_ == ENUMERABLE_STRINGS) {
Handle<FixedArray> enum_keys = Handle<FixedArray> enum_keys;
KeyAccumulator::GetEnumPropertyKeys(isolate_, object); if (object->HasFastProperties()) {
enum_keys = KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, object);
// If the number of properties equals the length of enumerable properties
// we do not have to filter out non-enumerable ones
Map* map = object->map();
int nof_descriptors = map->NumberOfOwnDescriptors();
if (enum_keys->length() != nof_descriptors) {
Handle<DescriptorArray> descs =
Handle<DescriptorArray>(map->instance_descriptors(), isolate_);
for (int i = 0; i < nof_descriptors; i++) {
PropertyDetails details = descs->GetDetails(i);
if (!details.IsDontEnum()) continue;
Object* key = descs->GetKey(i);
this->AddShadowKey(key);
}
}
} else if (object->IsJSGlobalObject()) {
enum_keys = GetOwnEnumPropertyDictionaryKeys(
isolate_, mode_, this, object, object->global_dictionary());
} else {
enum_keys = GetOwnEnumPropertyDictionaryKeys(
isolate_, mode_, this, object, object->property_dictionary());
}
AddKeys(enum_keys, DO_NOT_CONVERT); AddKeys(enum_keys, DO_NOT_CONVERT);
} else { } else {
if (object->HasFastProperties()) { if (object->HasFastProperties()) {
...@@ -538,10 +616,10 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver, ...@@ -538,10 +616,10 @@ Maybe<bool> KeyAccumulator::CollectOwnPropertyNames(Handle<JSReceiver> receiver,
} }
} else if (object->IsJSGlobalObject()) { } else if (object->IsJSGlobalObject()) {
GlobalDictionary::CollectKeysTo( GlobalDictionary::CollectKeysTo(
handle(object->global_dictionary(), isolate_), this, filter_); handle(object->global_dictionary(), isolate_), this);
} else { } else {
NameDictionary::CollectKeysTo( NameDictionary::CollectKeysTo(
handle(object->property_dictionary(), isolate_), this, filter_); handle(object->property_dictionary(), isolate_), this);
} }
} }
// Add the property keys from the interceptor. // Add the property keys from the interceptor.
...@@ -608,28 +686,18 @@ Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver, ...@@ -608,28 +686,18 @@ Maybe<bool> KeyAccumulator::CollectOwnKeys(Handle<JSReceiver> receiver,
} }
// static // static
Handle<FixedArray> KeyAccumulator::GetEnumPropertyKeys( Handle<FixedArray> KeyAccumulator::GetOwnEnumPropertyKeys(
Isolate* isolate, Handle<JSObject> object) { Isolate* isolate, Handle<JSObject> object) {
if (object->HasFastProperties()) { if (object->HasFastProperties()) {
return GetFastEnumPropertyKeys(isolate, object); return GetFastEnumPropertyKeys(isolate, object);
} else if (object->IsJSGlobalObject()) { } else if (object->IsJSGlobalObject()) {
Handle<GlobalDictionary> dictionary(object->global_dictionary(), isolate); return GetOwnEnumPropertyDictionaryKeys(
int length = dictionary->NumberOfEnumElements(); isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
if (length == 0) { object->global_dictionary());
return isolate->factory()->empty_fixed_array();
}
Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
dictionary->CopyEnumKeysTo(*storage);
return storage;
} else { } else {
Handle<NameDictionary> dictionary(object->property_dictionary(), isolate); return GetOwnEnumPropertyDictionaryKeys(
int length = dictionary->NumberOfEnumElements(); isolate, KeyCollectionMode::kOwnOnly, nullptr, object,
if (length == 0) { object->property_dictionary());
return isolate->factory()->empty_fixed_array();
}
Handle<FixedArray> storage = isolate->factory()->NewFixedArray(length);
dictionary->CopyEnumKeysTo(*storage);
return storage;
} }
} }
......
...@@ -53,8 +53,8 @@ class KeyAccumulator final BASE_EMBEDDED { ...@@ -53,8 +53,8 @@ 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);
static Handle<FixedArray> GetEnumPropertyKeys(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);
...@@ -64,13 +64,27 @@ class KeyAccumulator final BASE_EMBEDDED { ...@@ -64,13 +64,27 @@ class KeyAccumulator final BASE_EMBEDDED {
// Jump to the next level, pushing the current |levelLength_| to // Jump to the next level, pushing the current |levelLength_| to
// |levelLengths_| and adding a new list to |elements_|. // |levelLengths_| and adding a new list to |elements_|.
Isolate* isolate() { return isolate_; } Isolate* isolate() { return isolate_; }
// Filter keys based on their property descriptors.
PropertyFilter filter() { return filter_; } PropertyFilter filter() { return filter_; }
// The collection mode defines whether we collect the keys from the prototype
// chain or only look at the receiver.
KeyCollectionMode mode() { return mode_; }
void set_filter_proxy_keys(bool filter) { filter_proxy_keys_ = filter; } void set_filter_proxy_keys(bool filter) { filter_proxy_keys_ = filter; }
// In case of for-in loops we have to treat JSProxy keys differently and
// deduplicate them. Additionally we convert JSProxy keys back to array
// indices.
void set_is_for_in(bool value) { is_for_in_ = value; } void set_is_for_in(bool value) { is_for_in_ = value; }
void set_skip_indices(bool value) { skip_indices_ = value; } void set_skip_indices(bool value) { skip_indices_ = value; }
// The last_non_empty_prototype is used to limit the prototypes for which
// we have to keep track of non-enumerable keys that can shadow keys
// repeated on the prototype chain.
void set_last_non_empty_prototype(Handle<JSReceiver> object) { void set_last_non_empty_prototype(Handle<JSReceiver> object) {
last_non_empty_prototype_ = object; last_non_empty_prototype_ = object;
} }
// Shadowing keys are used to filter keys. This happens when non-enumerable
// keys appear again on the prototype chain.
void AddShadowKey(Object* key);
void AddShadowKey(Handle<Object> key);
private: private:
Maybe<bool> CollectOwnKeys(Handle<JSReceiver> receiver, Maybe<bool> CollectOwnKeys(Handle<JSReceiver> receiver,
...@@ -79,17 +93,18 @@ class KeyAccumulator final BASE_EMBEDDED { ...@@ -79,17 +93,18 @@ class KeyAccumulator final BASE_EMBEDDED {
Handle<JSProxy> proxy); Handle<JSProxy> proxy);
Maybe<bool> CollectOwnJSProxyTargetKeys(Handle<JSProxy> proxy, Maybe<bool> CollectOwnJSProxyTargetKeys(Handle<JSProxy> proxy,
Handle<JSReceiver> target); Handle<JSReceiver> target);
Maybe<bool> AddKeysFromJSProxy(Handle<JSProxy> proxy, Maybe<bool> AddKeysFromJSProxy(Handle<JSProxy> proxy,
Handle<FixedArray> keys); Handle<FixedArray> keys);
bool IsShadowed(Handle<Object> key);
Handle<OrderedHashSet> keys() { return Handle<OrderedHashSet>::cast(keys_); } Handle<OrderedHashSet> keys() { return Handle<OrderedHashSet>::cast(keys_); }
Isolate* isolate_; Isolate* isolate_;
// keys_ is either an Handle<OrderedHashSet> or in the case of own JSProxy // keys_ is either an Handle<OrderedHashSet> or in the case of own JSProxy
// keys a Handle<FixedArray>. // keys a Handle<FixedArray>. The OrderedHashSet is in-place converted to the
// result list, a FixedArray containing all collected keys.
Handle<FixedArray> keys_; Handle<FixedArray> keys_;
Handle<JSReceiver> last_non_empty_prototype_; Handle<JSReceiver> last_non_empty_prototype_;
Handle<ObjectHashSet> shadowed_keys_;
KeyCollectionMode mode_; KeyCollectionMode mode_;
PropertyFilter filter_; PropertyFilter filter_;
bool filter_proxy_keys_ = true; bool filter_proxy_keys_ = true;
...@@ -101,7 +116,8 @@ class KeyAccumulator final BASE_EMBEDDED { ...@@ -101,7 +116,8 @@ class KeyAccumulator final BASE_EMBEDDED {
// The FastKeyAccumulator handles the cases where there are no elements on the // The FastKeyAccumulator handles the cases where there are no elements on the
// prototype chain and forwords the complex/slow cases to the normal // prototype chain and forwords the complex/slow cases to the normal
// KeyAccumulator. // KeyAccumulator. This significantly speeds up the cases where the OWN_ONLY
// case where we do not have to walk the prototype chain.
class FastKeyAccumulator { class FastKeyAccumulator {
public: public:
FastKeyAccumulator(Isolate* isolate, Handle<JSReceiver> receiver, FastKeyAccumulator(Isolate* isolate, Handle<JSReceiver> receiver,
......
...@@ -286,7 +286,7 @@ Handle<Object> CallSite::GetMethodName() { ...@@ -286,7 +286,7 @@ Handle<Object> CallSite::GetMethodName() {
Handle<JSObject> current_obj = Handle<JSObject>::cast(current); Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
if (current_obj->IsAccessCheckNeeded()) break; if (current_obj->IsAccessCheckNeeded()) break;
Handle<FixedArray> keys = Handle<FixedArray> keys =
KeyAccumulator::GetEnumPropertyKeys(isolate_, current_obj); KeyAccumulator::GetOwnEnumPropertyKeys(isolate_, current_obj);
for (int i = 0; i < keys->length(); i++) { for (int i = 0; i < keys->length(); i++) {
HandleScope inner_scope(isolate_); HandleScope inner_scope(isolate_);
if (!keys->get(i)->IsName()) continue; if (!keys->get(i)->IsName()) continue;
......
...@@ -859,6 +859,8 @@ bool HeapObject::IsStringTable() const { return IsHashTable(); } ...@@ -859,6 +859,8 @@ bool HeapObject::IsStringTable() const { return IsHashTable(); }
bool HeapObject::IsStringSet() const { return IsHashTable(); } bool HeapObject::IsStringSet() const { return IsHashTable(); }
bool HeapObject::IsObjectHashSet() const { return IsHashTable(); }
bool HeapObject::IsNormalizedMapCache() const { bool HeapObject::IsNormalizedMapCache() const {
return NormalizedMapCache::IsNormalizedMapCache(this); return NormalizedMapCache::IsNormalizedMapCache(this);
} }
...@@ -3073,7 +3075,6 @@ int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key) { ...@@ -3073,7 +3075,6 @@ int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key) {
return FindEntry(isolate, key, HashTable::Hash(key)); return FindEntry(isolate, key, HashTable::Hash(key));
} }
// Find entry for key otherwise return kNotFound. // Find entry for key otherwise return kNotFound.
template <typename Derived, typename Shape, typename Key> template <typename Derived, typename Shape, typename Key>
int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key, int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key,
...@@ -3095,6 +3096,26 @@ int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key, ...@@ -3095,6 +3096,26 @@ int HashTable<Derived, Shape, Key>::FindEntry(Isolate* isolate, Key key,
return kNotFound; return kNotFound;
} }
template <typename Derived, typename Shape, typename Key>
bool HashTable<Derived, Shape, Key>::Has(Key key) {
return FindEntry(key) != kNotFound;
}
template <typename Derived, typename Shape, typename Key>
bool HashTable<Derived, Shape, Key>::Has(Isolate* isolate, Key key) {
return FindEntry(isolate, key) != kNotFound;
}
bool ObjectHashSet::Has(Isolate* isolate, Handle<Object> key, int32_t hash) {
return FindEntry(isolate, key, hash) != kNotFound;
}
bool ObjectHashSet::Has(Isolate* isolate, Handle<Object> key) {
Object* hash = key->GetHash();
if (!hash->IsSmi()) return false;
return FindEntry(isolate, key, Smi::cast(hash)->value()) != kNotFound;
}
bool StringSetShape::IsMatch(String* key, Object* value) { bool StringSetShape::IsMatch(String* key, Object* value) {
return value->IsString() && key->Equals(String::cast(value)); return value->IsString() && key->Equals(String::cast(value));
} }
...@@ -3191,6 +3212,7 @@ CAST_ACCESSOR(NameDictionary) ...@@ -3191,6 +3212,7 @@ CAST_ACCESSOR(NameDictionary)
CAST_ACCESSOR(NormalizedMapCache) CAST_ACCESSOR(NormalizedMapCache)
CAST_ACCESSOR(Object) CAST_ACCESSOR(Object)
CAST_ACCESSOR(ObjectHashTable) CAST_ACCESSOR(ObjectHashTable)
CAST_ACCESSOR(ObjectHashSet)
CAST_ACCESSOR(Oddball) CAST_ACCESSOR(Oddball)
CAST_ACCESSOR(OrderedHashMap) CAST_ACCESSOR(OrderedHashMap)
CAST_ACCESSOR(OrderedHashSet) CAST_ACCESSOR(OrderedHashSet)
......
...@@ -16515,6 +16515,11 @@ template Handle<NameDictionary> ...@@ -16515,6 +16515,11 @@ template Handle<NameDictionary>
HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >:: HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >::
New(Isolate*, int, MinimumCapacity, PretenureFlag); New(Isolate*, int, MinimumCapacity, PretenureFlag);
template Handle<ObjectHashSet> HashTable<ObjectHashSet, ObjectHashSetShape,
Handle<Object>>::New(Isolate*, int n,
MinimumCapacity,
PretenureFlag);
template Handle<NameDictionary> template Handle<NameDictionary>
HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >:: HashTable<NameDictionary, NameDictionaryShape, Handle<Name> >::
Shrink(Handle<NameDictionary>, Handle<Name>); Shrink(Handle<NameDictionary>, Handle<Name>);
...@@ -16585,24 +16590,33 @@ template int Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: ...@@ -16585,24 +16590,33 @@ template int Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::
template int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>:: template int Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::
NumberOfElementsFilterAttributes(PropertyFilter filter); NumberOfElementsFilterAttributes(PropertyFilter filter);
template void Dictionary<GlobalDictionary, GlobalDictionaryShape, template void
Handle<Name>>::CopyEnumKeysTo(FixedArray* storage); Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::
CopyEnumKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape,
Handle<Name>>>
dictionary,
Handle<FixedArray> storage, KeyCollectionMode mode,
KeyAccumulator* accumulator);
template void Dictionary<NameDictionary, NameDictionaryShape, template void
Handle<Name>>::CopyEnumKeysTo(FixedArray* storage); Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CopyEnumKeysTo(
Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>>
dictionary,
Handle<FixedArray> storage, KeyCollectionMode mode,
KeyAccumulator* accumulator);
template void template void
Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>:: Dictionary<GlobalDictionary, GlobalDictionaryShape, Handle<Name>>::
CollectKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape, CollectKeysTo(Handle<Dictionary<GlobalDictionary, GlobalDictionaryShape,
Handle<Name>>> Handle<Name>>>
dictionary, dictionary,
KeyAccumulator* keys, PropertyFilter filter); KeyAccumulator* keys);
template void template void
Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CollectKeysTo( Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>::CollectKeysTo(
Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>> Handle<Dictionary<NameDictionary, NameDictionaryShape, Handle<Name>>>
dictionary, dictionary,
KeyAccumulator* keys, PropertyFilter filter); KeyAccumulator* keys);
Handle<Object> JSObject::PrepareSlowElementsForSort( Handle<Object> JSObject::PrepareSlowElementsForSort(
Handle<JSObject> object, uint32_t limit) { Handle<JSObject> object, uint32_t limit) {
...@@ -17099,6 +17113,20 @@ bool StringSet::Has(Handle<String> name) { ...@@ -17099,6 +17113,20 @@ bool StringSet::Has(Handle<String> name) {
return FindEntry(*name) != kNotFound; return FindEntry(*name) != kNotFound;
} }
Handle<ObjectHashSet> ObjectHashSet::Add(Handle<ObjectHashSet> set,
Handle<Object> key) {
Isolate* isolate = set->GetIsolate();
int32_t hash = Object::GetOrCreateHash(isolate, key)->value();
if (!set->Has(isolate, key, hash)) {
set = EnsureCapacity(set, 1, key);
int entry = set->FindInsertionEntry(hash);
set->set(EntryToIndex(entry), *key);
set->ElementAdded();
}
return set;
}
Handle<Object> CompilationCacheTable::Lookup(Handle<String> src, Handle<Object> CompilationCacheTable::Lookup(Handle<String> src,
Handle<Context> context, Handle<Context> context,
LanguageMode language_mode) { LanguageMode language_mode) {
...@@ -17554,43 +17582,61 @@ struct EnumIndexComparator { ...@@ -17554,43 +17582,61 @@ struct EnumIndexComparator {
Dictionary* dict; Dictionary* dict;
}; };
template <typename Derived, typename Shape, typename Key> template <typename Derived, typename Shape, typename Key>
void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo(FixedArray* storage) { void Dictionary<Derived, Shape, Key>::CopyEnumKeysTo(
Isolate* isolate = this->GetIsolate(); Handle<Dictionary<Derived, Shape, Key>> dictionary,
Handle<FixedArray> storage, KeyCollectionMode mode,
KeyAccumulator* accumulator) {
Isolate* isolate = dictionary->GetIsolate();
int length = storage->length(); int length = storage->length();
int capacity = this->Capacity(); int capacity = dictionary->Capacity();
int properties = 0; int properties = 0;
for (int i = 0; i < capacity; i++) { for (int i = 0; i < capacity; i++) {
Object* k = this->KeyAt(i); Object* key = dictionary->KeyAt(i);
if (this->IsKey(isolate, k) && !k->IsSymbol()) { bool is_shadowing_key = false;
PropertyDetails details = this->DetailsAt(i); if (!dictionary->IsKey(isolate, key)) continue;
if (details.IsDontEnum() || this->IsDeleted(i)) continue; if (key->IsSymbol()) continue;
PropertyDetails details = dictionary->DetailsAt(i);
if (details.IsDontEnum()) {
if (mode == KeyCollectionMode::kIncludePrototypes) {
is_shadowing_key = true;
} else {
continue;
}
}
if (dictionary->IsDeleted(i)) continue;
if (is_shadowing_key) {
accumulator->AddShadowKey(key);
continue;
} else {
storage->set(properties, Smi::FromInt(i)); storage->set(properties, Smi::FromInt(i));
properties++;
if (properties == length) break;
} }
properties++;
if (properties == length) break;
} }
CHECK_EQ(length, properties); CHECK_EQ(length, properties);
EnumIndexComparator<Derived> cmp(static_cast<Derived*>(this)); DisallowHeapAllocation no_gc;
Dictionary<Derived, Shape, Key>* raw_dictionary = *dictionary;
FixedArray* raw_storage = *storage;
EnumIndexComparator<Derived> cmp(static_cast<Derived*>(*dictionary));
Smi** start = reinterpret_cast<Smi**>(storage->GetFirstElementAddress()); Smi** start = reinterpret_cast<Smi**>(storage->GetFirstElementAddress());
std::sort(start, start + length, cmp); std::sort(start, start + length, cmp);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
int index = Smi::cast(storage->get(i))->value(); int index = Smi::cast(raw_storage->get(i))->value();
storage->set(i, this->KeyAt(index)); raw_storage->set(i, raw_dictionary->KeyAt(index));
} }
} }
template <typename Derived, typename Shape, typename Key> template <typename Derived, typename Shape, typename Key>
void Dictionary<Derived, Shape, Key>::CollectKeysTo( void Dictionary<Derived, Shape, Key>::CollectKeysTo(
Handle<Dictionary<Derived, Shape, Key> > dictionary, KeyAccumulator* keys, Handle<Dictionary<Derived, Shape, Key>> dictionary, KeyAccumulator* keys) {
PropertyFilter filter) {
Isolate* isolate = keys->isolate(); Isolate* isolate = keys->isolate();
int capacity = dictionary->Capacity(); int capacity = dictionary->Capacity();
Handle<FixedArray> array = Handle<FixedArray> array =
isolate->factory()->NewFixedArray(dictionary->NumberOfElements()); isolate->factory()->NewFixedArray(dictionary->NumberOfElements());
int array_size = 0; int array_size = 0;
PropertyFilter filter = keys->filter();
{ {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
Dictionary<Derived, Shape, Key>* raw_dict = *dictionary; Dictionary<Derived, Shape, Key>* raw_dict = *dictionary;
...@@ -17599,7 +17645,10 @@ void Dictionary<Derived, Shape, Key>::CollectKeysTo( ...@@ -17599,7 +17645,10 @@ void Dictionary<Derived, Shape, Key>::CollectKeysTo(
if (!raw_dict->IsKey(isolate, k) || k->FilterKey(filter)) continue; if (!raw_dict->IsKey(isolate, k) || k->FilterKey(filter)) continue;
if (raw_dict->IsDeleted(i)) continue; if (raw_dict->IsDeleted(i)) continue;
PropertyDetails details = raw_dict->DetailsAt(i); PropertyDetails details = raw_dict->DetailsAt(i);
if ((details.attributes() & filter) != 0) continue; if ((details.attributes() & filter) != 0) {
keys->AddShadowKey(k);
continue;
}
if (filter & ONLY_ALL_CAN_READ) { if (filter & ONLY_ALL_CAN_READ) {
if (details.kind() != kAccessor) continue; if (details.kind() != kAccessor) continue;
Object* accessors = raw_dict->ValueAt(i); Object* accessors = raw_dict->ValueAt(i);
......
...@@ -1002,6 +1002,7 @@ template <class C> inline bool Is(Object* obj); ...@@ -1002,6 +1002,7 @@ template <class C> inline bool Is(Object* obj);
V(PropertyCell) \ V(PropertyCell) \
V(WeakCell) \ V(WeakCell) \
V(ObjectHashTable) \ V(ObjectHashTable) \
V(ObjectHashSet) \
V(WeakHashTable) \ V(WeakHashTable) \
V(OrderedHashTable) V(OrderedHashTable)
...@@ -3230,6 +3231,8 @@ class HashTable : public HashTableBase { ...@@ -3230,6 +3231,8 @@ class HashTable : public HashTableBase {
inline int FindEntry(Key key); inline int FindEntry(Key key);
inline int FindEntry(Isolate* isolate, Key key, int32_t hash); inline int FindEntry(Isolate* isolate, Key key, int32_t hash);
int FindEntry(Isolate* isolate, Key key); int FindEntry(Isolate* isolate, Key key);
inline bool Has(Isolate* isolate, Key key);
inline bool Has(Key key);
// Rehashes the table in-place. // Rehashes the table in-place.
void Rehash(Key key); void Rehash(Key key);
...@@ -3456,11 +3459,13 @@ class Dictionary: public HashTable<Derived, Shape, Key> { ...@@ -3456,11 +3459,13 @@ class Dictionary: public HashTable<Derived, Shape, Key> {
// Collect the keys into the given KeyAccumulator, in ascending chronological // Collect the keys into the given KeyAccumulator, in ascending chronological
// order of property creation. // order of property creation.
static void CollectKeysTo(Handle<Dictionary<Derived, Shape, Key> > dictionary, static void CollectKeysTo(Handle<Dictionary<Derived, Shape, Key>> dictionary,
KeyAccumulator* keys, PropertyFilter filter); KeyAccumulator* keys);
// Copies enumerable keys to preallocated fixed array. // Copies enumerable keys to preallocated fixed array.
void CopyEnumKeysTo(FixedArray* storage); static void CopyEnumKeysTo(Handle<Dictionary<Derived, Shape, Key>> dictionary,
Handle<FixedArray> storage, KeyCollectionMode mode,
KeyAccumulator* accumulator);
// Accessors for next enumeration index. // Accessors for next enumeration index.
void SetNextEnumerationIndex(int index) { void SetNextEnumerationIndex(int index) {
...@@ -3806,6 +3811,23 @@ class ObjectHashTable: public HashTable<ObjectHashTable, ...@@ -3806,6 +3811,23 @@ class ObjectHashTable: public HashTable<ObjectHashTable,
} }
}; };
class ObjectHashSetShape : public ObjectHashTableShape {
public:
static const int kPrefixSize = 0;
static const int kEntrySize = 1;
};
class ObjectHashSet
: public HashTable<ObjectHashSet, ObjectHashSetShape, Handle<Object>> {
public:
static Handle<ObjectHashSet> Add(Handle<ObjectHashSet> set,
Handle<Object> key);
inline bool Has(Isolate* isolate, Handle<Object> key, int32_t hash);
inline bool Has(Isolate* isolate, Handle<Object> key);
DECLARE_CAST(ObjectHashSet)
};
// OrderedHashTable is a HashTable with Object keys that preserves // OrderedHashTable is a HashTable with Object keys that preserves
// insertion order. There are Map and Set interfaces (OrderedHashMap // insertion order. There are Map and Set interfaces (OrderedHashMap
......
...@@ -113,6 +113,74 @@ TEST(HashMap) { ...@@ -113,6 +113,74 @@ TEST(HashMap) {
TestHashMap(ObjectHashTable::New(isolate, 23)); TestHashMap(ObjectHashTable::New(isolate, 23));
} }
template <typename HashSet>
static void TestHashSet(Handle<HashSet> table) {
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
Handle<JSObject> a = factory->NewJSArray(7);
Handle<JSObject> b = factory->NewJSArray(11);
table = HashSet::Add(table, a);
CHECK_EQ(table->NumberOfElements(), 1);
CHECK(table->Has(isolate, a));
CHECK(!table->Has(isolate, b));
// Keys still have to be valid after objects were moved.
CcTest::heap()->CollectGarbage(NEW_SPACE);
CHECK_EQ(table->NumberOfElements(), 1);
CHECK(table->Has(isolate, a));
CHECK(!table->Has(isolate, b));
// Keys that are overwritten should not change number of elements.
table = HashSet::Add(table, a);
CHECK_EQ(table->NumberOfElements(), 1);
CHECK(table->Has(isolate, a));
CHECK(!table->Has(isolate, b));
// Keys that have been removed are mapped to the hole.
// TODO(cbruni): not implemented yet.
// bool was_present = false;
// table = HashSet::Remove(table, a, &was_present);
// CHECK(was_present);
// CHECK_EQ(table->NumberOfElements(), 0);
// CHECK(!table->Has(a));
// CHECK(!table->Has(b));
// Keys should map back to their respective values and also should get
// an identity hash code generated.
for (int i = 0; i < 100; i++) {
Handle<JSReceiver> key = factory->NewJSArray(7);
table = HashSet::Add(table, key);
CHECK_EQ(table->NumberOfElements(), i + 2);
CHECK(table->Has(isolate, key));
CHECK(JSReceiver::GetIdentityHash(isolate, key)->IsSmi());
}
// Keys never added to the map which already have an identity hash
// code should not be found.
for (int i = 0; i < 100; i++) {
Handle<JSReceiver> key = factory->NewJSArray(7);
CHECK(JSReceiver::GetOrCreateIdentityHash(isolate, key)->IsSmi());
CHECK(!table->Has(isolate, key));
CHECK(JSReceiver::GetIdentityHash(isolate, key)->IsSmi());
}
// Keys that don't have an identity hash should not be found and also
// should not get an identity hash code generated.
for (int i = 0; i < 100; i++) {
Handle<JSReceiver> key = factory->NewJSArray(7);
CHECK(!table->Has(isolate, key));
Object* identity_hash = JSReceiver::GetIdentityHash(isolate, key);
CHECK_EQ(CcTest::heap()->undefined_value(), identity_hash);
}
}
TEST(HashSet) {
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Isolate* isolate = CcTest::i_isolate();
TestHashSet(ObjectHashSet::New(isolate, 23));
}
class ObjectHashTableTest: public ObjectHashTable { class ObjectHashTableTest: public ObjectHashTable {
public: public:
......
...@@ -30,61 +30,136 @@ ...@@ -30,61 +30,136 @@
function props(x) { function props(x) {
var array = []; var array = [];
for (var p in x) array.push(p); for (var p in x) array.push(p);
return array.sort(); return array;
} }
assertEquals(0, props({}).length, "olen0"); (function forInBasic() {
assertEquals(1, props({x:1}).length, "olen1"); assertEquals(0, props({}).length, "olen0");
assertEquals(2, props({x:1, y:2}).length, "olen2"); assertEquals(1, props({x:1}).length, "olen1");
assertEquals(2, props({x:1, y:2}).length, "olen2");
assertArrayEquals(["x"], props({x:1}), "x"); assertArrayEquals(["x"], props({x:1}), "x");
assertArrayEquals(["x", "y"], props({x:1, y:2}), "xy"); assertArrayEquals(["x", "y"], props({x:1, y:2}), "xy");
assertArrayEquals(["x", "y", "zoom"], props({x:1, y:2, zoom:3}), "xyzoom"); assertArrayEquals(["x", "y", "zoom"], props({x:1, y:2, zoom:3}), "xyzoom");
assertEquals(0, props([]).length, "alen0"); assertEquals(0, props([]).length, "alen0");
assertEquals(1, props([1]).length, "alen1"); assertEquals(1, props([1]).length, "alen1");
assertEquals(2, props([1,2]).length, "alen2"); assertEquals(2, props([1,2]).length, "alen2");
assertArrayEquals(["0"], props([1]), "0"); assertArrayEquals(["0"], props([1]), "0");
assertArrayEquals(["0", "1"], props([1,2]), "01"); assertArrayEquals(["0", "1"], props([1,2]), "01");
assertArrayEquals(["0", "1", "2"], props([1,2,3]), "012"); assertArrayEquals(["0", "1", "2"], props([1,2,3]), "012");
})();
var o = {}; (function forInPrototype() {
var a = []; // Fast properties + fast elements
for (var i = 0x0020; i < 0x01ff; i+=2) { var obj = {a:true, 3:true, 4:true};
var s = 'char:' + String.fromCharCode(i); obj.__proto__ = {c:true, b:true, 2:true, 1:true, 5:true};
a.push(s); for (var i = 0; i < 3; i++) {
o[s] = i; assertArrayEquals("34a125cb".split(""), props(obj));
} }
assertArrayEquals(a, props(o), "charcodes"); // Fast properties + dictionary elements
delete obj.__proto__[2];
var a = []; for (var i = 0; i < 3; i++) {
assertEquals(0, props(a).length, "proplen0"); assertArrayEquals("34a15cb".split(""), props(obj));
a[Math.pow(2,30)-1] = 0; }
assertEquals(1, props(a).length, "proplen1"); // Slow properties + dictionary elements
a[Math.pow(2,31)-1] = 0; delete obj.__proto__.c;
assertEquals(2, props(a).length, "proplen2"); for (var i = 0; i < 3; i++) {
a[1] = 0; assertArrayEquals("34a15b".split(""), props(obj));
assertEquals(3, props(a).length, "proplen3"); }
// Slow properties on the receiver as well
for (var hest = 'hest' in {}) { } delete obj.a;
assertEquals('hest', hest, "empty-no-override"); for (var i = 0; i < 3; i++) {
assertArrayEquals("3415b".split(""), props(obj));
var result = ''; }
for (var p in {a : [0], b : 1}) { result += p; } delete obj[3];
assertEquals('ab', result, "ab"); for (var i = 0; i < 3; i++) {
assertArrayEquals("415b".split(""), props(obj));
var result = ''; }
for (var p in {a : {v:1}, b : 1}) { result += p; } })();
assertEquals('ab', result, "ab-nodeep");
(function forInShadowing() {
var result = ''; var obj = {a:true, 3:true, 4:true};
for (var p in { get a() {}, b : 1}) { result += p; } obj.__proto__ = {
assertEquals('ab', result, "abget"); c:true, b:true, x:true,
2:true, 1:true, 5:true, 9:true};
var result = ''; Object.defineProperty(obj, 'x', {value:true, enumerable:false, configurable:true});
for (var p in { get a() {}, set a(x) {}, b : 1}) { result += p; } Object.defineProperty(obj, '9', {value:true, enumerable:false, configurable:true});
assertEquals('ab', result, "abgetset"); for (var i = 0; i < 3; i++) {
assertArrayEquals("34a125cb".split(""), props(obj));
}
// Fast properties + dictionary elements
delete obj.__proto__[2];
for (var i = 0; i < 3; i++) {
assertArrayEquals("34a15cb".split(""), props(obj));
}
// Slow properties + dictionary elements
delete obj.__proto__.c;
for (var i = 0; i < 3; i++) {
assertArrayEquals("34a15b".split(""), props(obj));
}
// Remove the shadowing properties
delete obj.x;
delete obj[9];
for (var i = 0; i < 3; i++) {
assertArrayEquals("34a159bx".split(""), props(obj));
}
// Slow properties on the receiver as well
delete obj.a;
for (var i = 0; i < 3; i++) {
assertArrayEquals("34159bx".split(""), props(obj));
}
delete obj[3];
for (var i = 0; i < 3; i++) {
assertArrayEquals("4159bx".split(""), props(obj));
}
})();
(function forInCharCodes() {
var o = {};
var a = [];
for (var i = 0x0020; i < 0x01ff; i+=2) {
var s = 'char:' + String.fromCharCode(i);
a.push(s);
o[s] = i;
}
assertArrayEquals(a, props(o), "charcodes");
})();
(function forInArray() {
var a = [];
assertEquals(0, props(a).length, "proplen0");
a[Math.pow(2,30)-1] = 0;
assertEquals(1, props(a).length, "proplen1");
a[Math.pow(2,31)-1] = 0;
assertEquals(2, props(a).length, "proplen2");
a[1] = 0;
assertEquals(3, props(a).length, "proplen3");
})();
(function forInInitialize() {
for (var hest = 'hest' in {}) { }
assertEquals('hest', hest, "empty-no-override");
})();
(function forInObjects() {
var result = '';
for (var p in {a : [0], b : 1}) { result += p; }
assertEquals('ab', result, "ab");
var result = '';
for (var p in {a : {v:1}, b : 1}) { result += p; }
assertEquals('ab', result, "ab-nodeep");
var result = '';
for (var p in { get a() {}, b : 1}) { result += p; }
assertEquals('ab', result, "abget");
var result = '';
for (var p in { get a() {}, set a(x) {}, b : 1}) { result += p; }
assertEquals('ab', result, "abgetset");
})();
// Test that for-in in the global scope works with a keyed property as "each". // Test that for-in in the global scope works with a keyed property as "each".
......
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