Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 9bf72a68d7e42349c821d6883578f0725a6d8c13..1a0372ffa8c444b6bc68b07d737d61002577c0fb 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -6480,11 +6480,130 @@ Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object, |
| } |
| +Handle<FixedArray> KeyAccumulator::GetKeys() { |
| + if (length_ == 0) { |
| + return isolate_->factory()->empty_fixed_array(); |
| + } |
| + if (set_.is_null()) { |
| + keys_->Shrink(length_); |
| + return keys_; |
| + } |
| + // copy over results from set_ |
| + Handle<FixedArray> result = isolate_->factory()->NewFixedArray(length_); |
| + for (int i = 0; i < length_; i++) { |
| + result->set(i, set_->KeyAt(i)); |
| + } |
| + return result; |
| +} |
| + |
| + |
| +int KeyAccumulator::GetLength() { return length_; } |
|
Igor Sheludko
2015/09/17 09:55:11
Please move this implementation to the class defin
|
| + |
| + |
| +void KeyAccumulator::AddKey(Handle<Object> key, int check_limit) { |
| + if (!set_.is_null()) { |
| + set_ = OrderedHashSet::Add(set_, key); |
| + length_ = set_->NumberOfElements(); |
| + return; |
| + } |
| + // check if we already have the key in the case we are still using |
| + // the keys_ FixedArray |
| + check_limit = Min(check_limit, length_); |
| + for (int i = 0; i < check_limit; i++) { |
| + Object* current = keys_->get(i); |
| + if (current->KeyEquals(*key)) return; |
| + } |
| + EnsureCapacity(length_); |
| + keys_->set(length_, *key); |
| + length_++; |
| +} |
| + |
| + |
| +void KeyAccumulator::AddKeys(Handle<FixedArray> array, |
| + FixedArray::KeyFilter filter) { |
| + int add_length = array->length(); |
| + if (add_length == 0) return; |
| + if (keys_.is_null() && filter == FixedArray::ALL_KEYS) { |
| + keys_ = array; |
| + length_ = keys_->length(); |
| + return; |
| + } |
| + PrepareForComparisons(add_length); |
| + int previous_key_count = length_; |
| + for (int i = 0; i < add_length; i++) { |
| + Handle<Object> current(array->get(i), isolate_); |
| + if (filter == FixedArray::NON_SYMBOL_KEYS && current->IsSymbol()) continue; |
| + AddKey(current, previous_key_count); |
| + } |
| +} |
| + |
| + |
| +void KeyAccumulator::AddKeys(Handle<JSObject> array_like, |
| + FixedArray::KeyFilter filter) { |
| + DCHECK(array_like->IsJSArray() || array_like->HasSloppyArgumentsElements()); |
| + ElementsAccessor* accessor = array_like->GetElementsAccessor(); |
| + accessor->AddElementsToFixedArrayWithAccumulator(array_like, this, filter); |
| +#ifdef ENABLE_SLOW_DCHECKS |
| + if (FLAG_enable_slow_asserts) { |
| + DisallowHeapAllocation no_allocation; |
| + for (int i = 0; i < GetLength(); i++) { |
| + Object* current = keys_->get(i); |
| + DCHECK(current->IsNumber() || current->IsName()); |
| + } |
| + } |
| +#endif |
| +} |
| + |
| + |
| +void KeyAccumulator::PrepareForComparisons(int count) { |
| + // Depending on how many comparisons we do we should switch to the |
| + // hash-table-based checks which have a one-time overhead for |
| + // initializing but O(1) for HasKey checks. |
| + if (!set_.is_null()) return; |
| + // This limit was obtained through evaluation of a microbench. |
| + if (length_ * count < 50) return; |
| + set_ = OrderedHashSet::Allocate(isolate_, length_); |
| + for (int i = 0; i < length_; i++) { |
| + Handle<Object> value(keys_->get(i), isolate_); |
| + set_ = OrderedHashSet::Add(set_, value); |
| + } |
| +} |
| + |
| + |
| +void KeyAccumulator::EnsureCapacity(int capacity) { |
| + if (keys_.is_null() || keys_->length() <= capacity) { |
| + Grow(); |
| + } |
| +} |
| + |
| + |
| +void KeyAccumulator::Grow() { |
| + // The OrderedHashSet handles growing by itself. |
| + if (!set_.is_null()) return; |
| + // Otherwise, grow the internal keys_ FixedArray |
| + int capacity = keys_.is_null() ? 16 : keys_->length() * 2 + 16; |
| + Handle<FixedArray> new_keys = isolate_->factory()->NewFixedArray(capacity); |
| + if (keys_.is_null()) { |
| + keys_ = new_keys; |
| + return; |
| + } |
| + int buffer_length = keys_->length(); |
| + { |
| + DisallowHeapAllocation no_gc; |
| + WriteBarrierMode mode = new_keys->GetWriteBarrierMode(no_gc); |
| + for (int i = 0; i < buffer_length; i++) { |
| + new_keys->set(i, keys_->get(i), mode); |
| + } |
| + } |
| + keys_ = new_keys; |
| +} |
| + |
| + |
| MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, |
| KeyCollectionType type) { |
| USE(ContainsOnlyValidKeys); |
| Isolate* isolate = object->GetIsolate(); |
| - Handle<FixedArray> content = isolate->factory()->empty_fixed_array(); |
| + KeyAccumulator accumulator(isolate); |
| Handle<JSFunction> arguments_function( |
| JSFunction::cast(isolate->sloppy_arguments_map()->GetConstructor())); |
| @@ -6507,11 +6626,8 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, |
| arraysize(args), |
| args), |
| FixedArray); |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, content, |
| - FixedArray::AddKeysFromArrayLike( |
| - content, Handle<JSObject>::cast(names)), |
| - FixedArray); |
| + accumulator.AddKeys(Handle<JSObject>::cast(names), |
| + FixedArray::NON_SYMBOL_KEYS); |
| break; |
| } |
| @@ -6530,23 +6646,17 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, |
| Handle<FixedArray> element_keys = |
| isolate->factory()->NewFixedArray(current->NumberOfEnumElements()); |
| current->GetEnumElementKeys(*element_keys); |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, content, |
| - FixedArray::UnionOfKeys(content, element_keys), |
| - FixedArray); |
| - DCHECK(ContainsOnlyValidKeys(content)); |
| + accumulator.AddKeys(element_keys, FixedArray::ALL_KEYS); |
| + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); |
| // Add the element keys from the interceptor. |
| if (current->HasIndexedInterceptor()) { |
| Handle<JSObject> result; |
| if (JSObject::GetKeysForIndexedInterceptor( |
| current, object).ToHandle(&result)) { |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, content, |
| - FixedArray::AddKeysFromArrayLike(content, result), |
| - FixedArray); |
| + accumulator.AddKeys(result, FixedArray::ALL_KEYS); |
| } |
| - DCHECK(ContainsOnlyValidKeys(content)); |
| + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); |
| } |
| // We can cache the computed property keys if access checks are |
| @@ -6564,27 +6674,26 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object, |
| !current->IsJSValue() && !current->IsAccessCheckNeeded() && |
| !current->HasNamedInterceptor() && !current->HasIndexedInterceptor()); |
| // Compute the property keys and cache them if possible. |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, content, |
| - FixedArray::UnionOfKeys( |
| - content, JSObject::GetEnumPropertyKeys(current, cache_enum_keys)), |
| - FixedArray); |
| - DCHECK(ContainsOnlyValidKeys(content)); |
| + |
| + Handle<FixedArray> enum_keys = |
| + JSObject::GetEnumPropertyKeys(current, cache_enum_keys); |
| + accumulator.AddKeys(enum_keys, FixedArray::ALL_KEYS); |
| + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); |
| // Add the non-symbol property keys from the interceptor. |
| if (current->HasNamedInterceptor()) { |
| Handle<JSObject> result; |
| if (JSObject::GetKeysForNamedInterceptor( |
| current, object).ToHandle(&result)) { |
| - ASSIGN_RETURN_ON_EXCEPTION( |
| - isolate, content, FixedArray::AddKeysFromArrayLike( |
| - content, result, FixedArray::NON_SYMBOL_KEYS), |
| - FixedArray); |
| + accumulator.AddKeys(result, FixedArray::NON_SYMBOL_KEYS); |
| } |
| - DCHECK(ContainsOnlyValidKeys(content)); |
| + DCHECK(ContainsOnlyValidKeys(accumulator.GetKeys())); |
| } |
| } |
| - return content; |
| + |
| + Handle<FixedArray> keys = accumulator.GetKeys(); |
| + DCHECK(ContainsOnlyValidKeys(keys)); |
| + return keys; |
| } |
| @@ -8085,53 +8194,6 @@ void FixedArray::Shrink(int new_length) { |
| } |
| -MaybeHandle<FixedArray> FixedArray::AddKeysFromArrayLike( |
| - Handle<FixedArray> content, Handle<JSObject> array, KeyFilter filter) { |
| - DCHECK(array->IsJSArray() || array->HasSloppyArgumentsElements()); |
| - ElementsAccessor* accessor = array->GetElementsAccessor(); |
| - Handle<FixedArray> result = |
| - accessor->AddElementsToFixedArray(array, content, filter); |
| - |
| -#ifdef ENABLE_SLOW_DCHECKS |
| - if (FLAG_enable_slow_asserts) { |
| - DisallowHeapAllocation no_allocation; |
| - for (int i = 0; i < result->length(); i++) { |
| - Object* current = result->get(i); |
| - DCHECK(current->IsNumber() || current->IsName()); |
| - } |
| - } |
| -#endif |
| - return result; |
| -} |
| - |
| - |
| -MaybeHandle<FixedArray> FixedArray::UnionOfKeys(Handle<FixedArray> first, |
| - Handle<FixedArray> second) { |
| - if (second->length() == 0) return first; |
| - if (first->length() == 0) return second; |
| - Isolate* isolate = first->GetIsolate(); |
| - Handle<FixedArray> result = |
| - isolate->factory()->NewFixedArray(first->length() + second->length()); |
| - for (int i = 0; i < first->length(); i++) { |
| - result->set(i, first->get(i)); |
| - } |
| - int pos = first->length(); |
| - for (int j = 0; j < second->length(); j++) { |
| - Object* current = second->get(j); |
| - int i; |
| - for (i = 0; i < first->length(); i++) { |
| - if (current->KeyEquals(first->get(i))) break; |
| - } |
| - if (i == first->length()) { |
| - result->set(pos++, current); |
| - } |
| - } |
| - |
| - result->Shrink(pos); |
| - return result; |
| -} |
| - |
| - |
| void FixedArray::CopyTo(int pos, FixedArray* dest, int dest_pos, int len) { |
| DisallowHeapAllocation no_gc; |
| WriteBarrierMode mode = dest->GetWriteBarrierMode(no_gc); |
| @@ -15261,10 +15323,10 @@ Object* ObjectHashTable::Lookup(Handle<Object> key, int32_t hash) { |
| Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, |
| Handle<Object> key, |
| Handle<Object> value) { |
| + Isolate* isolate = table->GetIsolate(); |
| DCHECK(table->IsKey(*key)); |
| DCHECK(!value->IsTheHole()); |
| - Isolate* isolate = table->GetIsolate(); |
| // Make sure the key object has an identity hash code. |
| int32_t hash = Object::GetOrCreateHash(isolate, key)->value(); |
| @@ -15276,11 +15338,10 @@ Handle<ObjectHashTable> ObjectHashTable::Put(Handle<ObjectHashTable> table, |
| Handle<Object> key, |
| Handle<Object> value, |
| int32_t hash) { |
| + Isolate* isolate = table->GetIsolate(); |
| DCHECK(table->IsKey(*key)); |
| DCHECK(!value->IsTheHole()); |
| - Isolate* isolate = table->GetIsolate(); |
| - |
| int entry = table->FindEntry(isolate, key, hash); |
| // Key is already in table, just overwrite value. |
| @@ -15454,6 +15515,48 @@ Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Clear( |
| return new_table; |
| } |
| +template <class Derived, class Iterator, int entrysize> |
| +bool OrderedHashTable<Derived, Iterator, entrysize>::HasKey( |
| + Handle<Derived> table, Handle<Object> key) { |
| + int entry = table->KeyToFirstEntry(*key); |
| + // Walk the chain in the bucket to find the key. |
| + while (entry != kNotFound) { |
| + Object* candidate_key = table->KeyAt(entry); |
| + if (candidate_key->SameValueZero(*key)) return true; |
| + entry = table->NextChainEntry(entry); |
| + } |
| + return false; |
| +} |
| + |
| + |
| +Handle<OrderedHashSet> OrderedHashSet::Add(Handle<OrderedHashSet> table, |
| + Handle<Object> key) { |
| + int hash = Object::GetOrCreateHash(table->GetIsolate(), key)->value(); |
| + int entry = table->HashToEntry(hash); |
| + // Walk the chain of the bucket and try finding the key. |
| + while (entry != kNotFound) { |
| + Object* candidate_key = table->KeyAt(entry); |
| + // Do not add if we have the key already |
| + if (candidate_key->SameValueZero(*key)) return table; |
| + entry = table->NextChainEntry(entry); |
| + } |
| + |
| + table = OrderedHashSet::EnsureGrowable(table); |
| + // Read the enxisting bucket values. |
|
Igor Sheludko
2015/09/17 09:55:11
s/enxisting/existing/
|
| + int bucket = table->HashToBucket(hash); |
| + int previous_entry = table->HashToEntry(hash); |
| + int nof = table->NumberOfElements(); |
| + // Insert a new entry at the end, |
| + int new_entry = nof + table->NumberOfDeletedElements(); |
| + int new_index = table->EntryToIndex(new_entry); |
| + table->set(new_index, *key); |
| + table->set(new_index + 1, Smi::FromInt(previous_entry)); |
| + // and point the bucket to the new entry. |
| + table->set(kHashTableStartIndex + bucket, Smi::FromInt(new_entry)); |
| + table->SetNumberOfElements(nof + 1); |
| + return table; |
| +} |
| + |
| template<class Derived, class Iterator, int entrysize> |
| Handle<Derived> OrderedHashTable<Derived, Iterator, entrysize>::Rehash( |
| @@ -15516,6 +15619,9 @@ template Handle<OrderedHashSet> |
| OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::Clear( |
| Handle<OrderedHashSet> table); |
| +template bool OrderedHashTable<OrderedHashSet, JSSetIterator, 1>::HasKey( |
| + Handle<OrderedHashSet> table, Handle<Object> key); |
| + |
| template Handle<OrderedHashMap> |
| OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Allocate( |
| @@ -15533,6 +15639,9 @@ template Handle<OrderedHashMap> |
| OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::Clear( |
| Handle<OrderedHashMap> table); |
| +template bool OrderedHashTable<OrderedHashMap, JSMapIterator, 2>::HasKey( |
| + Handle<OrderedHashMap> table, Handle<Object> key); |
| + |
| template<class Derived, class TableType> |
| void OrderedHashTableIterator<Derived, TableType>::Transition() { |