Chromium Code Reviews| Index: src/objects.cc |
| diff --git a/src/objects.cc b/src/objects.cc |
| index 31c5babec9047a9fa84460e46294b6359a52b50c..689c40923220334190f2c4fe26fc0fec87535202 100644 |
| --- a/src/objects.cc |
| +++ b/src/objects.cc |
| @@ -2845,22 +2845,26 @@ static bool HasKey(FixedArray* array, Object* key) { |
| Object* FixedArray::AddKeysFromJSArray(JSArray* array) { |
| - // Remove array holes from array if any. |
| - Object* object = array->RemoveHoles(); |
| - if (object->IsFailure()) return object; |
| - JSArray* compacted_array = JSArray::cast(object); |
| + if (array->HasFastElements()) { |
| + return UnionOfKeys(array->elements()); |
| + } |
| + ASSERT(!array->HasFastElements()); |
| + Dictionary* dict = array->element_dictionary(); |
| + int size = dict->NumberOfElements(); |
| // Allocate a temporary fixed array. |
| - int compacted_array_length = Smi::cast(compacted_array->length())->value(); |
| - object = Heap::AllocateFixedArray(compacted_array_length); |
| + Object* object = Heap::AllocateFixedArray(size); |
| if (object->IsFailure()) return object; |
| FixedArray* key_array = FixedArray::cast(object); |
| + int capacity = dict->Capacity(); |
| + int pos = 0; |
| // Copy the elements from the JSArray to the temporary fixed array. |
| - for (int i = 0; i < compacted_array_length; i++) { |
| - key_array->set(i, compacted_array->GetElement(i)); |
| + for (int i = 0; i < capacity; i++) { |
| + if (dict->IsKey(dict->KeyAt(i))) { |
| + key_array->set(pos++, dict->ValueAt(i)); |
| + } |
| } |
| - |
| // Compute the union of this and the temporary fixed array. |
| return UnionOfKeys(key_array); |
| } |
| @@ -2876,9 +2880,12 @@ Object* FixedArray::UnionOfKeys(FixedArray* other) { |
| // Compute how many elements are not in this. |
| int extra = 0; |
| for (int y = 0; y < len1; y++) { |
| - if (!HasKey(this, other->get(y))) extra++; |
| + Object* value = other->get(y); |
| + if (!value->IsTheHole() && !HasKey(this, value)) extra++; |
| } |
| + if (extra == 0) return this; |
| + |
| // Allocate the result |
| Object* obj = Heap::AllocateFixedArray(len0 + extra); |
| if (obj->IsFailure()) return obj; |
| @@ -2891,7 +2898,8 @@ Object* FixedArray::UnionOfKeys(FixedArray* other) { |
| // Fill in the extra keys. |
| int index = 0; |
| for (int y = 0; y < len1; y++) { |
| - if (!HasKey(this, other->get(y))) { |
| + Object* value = other->get(y); |
| + if (!value->IsTheHole() && !HasKey(this, value)) { |
| result->set(len0 + index, other->get(y), mode); |
| index++; |
| } |
| @@ -5623,75 +5631,18 @@ bool JSObject::ShouldConvertToFastElements() { |
| } |
| -Object* Dictionary::RemoveHoles() { |
| - int capacity = Capacity(); |
| - Object* obj = Allocate(NumberOfElements()); |
| - if (obj->IsFailure()) return obj; |
| - Dictionary* dict = Dictionary::cast(obj); |
| - uint32_t pos = 0; |
| - for (int i = 0; i < capacity; i++) { |
| - Object* k = KeyAt(i); |
| - if (IsKey(k)) { |
| - dict->AddNumberEntry(pos++, ValueAt(i), DetailsAt(i)); |
| - } |
| - } |
| - return dict; |
| -} |
| - |
| - |
| void Dictionary::CopyValuesTo(FixedArray* elements) { |
| int pos = 0; |
| int capacity = Capacity(); |
| + WriteBarrierMode mode = elements->GetWriteBarrierMode(); |
| for (int i = 0; i < capacity; i++) { |
| Object* k = KeyAt(i); |
| - if (IsKey(k)) elements->set(pos++, ValueAt(i)); |
| + if (IsKey(k)) elements->set(pos++, ValueAt(i), mode); |
| } |
| ASSERT(pos == elements->length()); |
| } |
| -Object* JSArray::RemoveHoles() { |
| - if (HasFastElements()) { |
| - int len = Smi::cast(length())->value(); |
| - int pos = 0; |
| - FixedArray* elms = FixedArray::cast(elements()); |
| - for (int index = 0; index < len; index++) { |
| - Object* e = elms->get(index); |
| - if (!e->IsTheHole()) { |
| - if (index != pos) elms->set(pos, e); |
| - pos++; |
| - } |
| - } |
| - set_length(Smi::FromInt(pos), SKIP_WRITE_BARRIER); |
| - for (int index = pos; index < len; index++) { |
| - elms->set_the_hole(index); |
| - } |
| - return this; |
| - } |
| - |
| - // Compact the sparse array if possible. |
| - Dictionary* dict = element_dictionary(); |
| - int length = dict->NumberOfElements(); |
| - |
| - // Try to make this a fast array again. |
| - if (length <= kMaxFastElementsLength) { |
| - Object* obj = Heap::AllocateFixedArray(length); |
| - if (obj->IsFailure()) return obj; |
| - dict->CopyValuesTo(FixedArray::cast(obj)); |
| - set_length(Smi::FromInt(length), SKIP_WRITE_BARRIER); |
| - set_elements(FixedArray::cast(obj)); |
| - return this; |
| - } |
| - |
| - // Make another dictionary with smaller indices. |
| - Object* obj = dict->RemoveHoles(); |
| - if (obj->IsFailure()) return obj; |
| - set_length(Smi::FromInt(length), SKIP_WRITE_BARRIER); |
| - set_elements(Dictionary::cast(obj)); |
| - return this; |
| -} |
| - |
| - |
| InterceptorInfo* JSObject::GetNamedInterceptor() { |
| ASSERT(map()->has_named_interceptor()); |
| JSFunction* constructor = JSFunction::cast(map()->constructor()); |
| @@ -6443,6 +6394,173 @@ template class HashTable<2, 3>; |
| template class HashTable<0, 2>; |
| +// Collates undefined and unexisting elements below limit from position |
| +// zero of the elements. The object stays in Dictionary mode. |
| +Object* JSObject::PrepareSlowElementsForSort(uint32_t limit) { |
| + static const uint32_t kMaxUInt32 = 4294967295u; |
|
Erik Corry
2009/04/27 09:42:13
This should probably go next to kMaxInt in globals
|
| + ASSERT(!HasFastElements()); |
| + // Must stay in dictionary mode, either because of requires_slow_elements, |
| + // or because we are not going to sort (and therefore compact) all of the |
| + // elements. |
| + Dictionary* dict = element_dictionary(); |
| + HeapNumber* result_double; |
|
Erik Corry
2009/04/27 09:42:13
Please initialize to NULL and ASSERT it is not nul
|
| + if (limit > static_cast<uint32_t>(Smi::kMaxValue)) { |
| + // Allocate space for result before we start mutating the object. |
| + Object* new_double = Heap::AllocateHeapNumber(0.0); |
| + if (new_double->IsFailure()) return new_double; |
| + result_double = HeapNumber::cast(new_double); |
| + } |
| + |
| + int capacity = dict->Capacity(); |
| + Object* obj = Dictionary::Allocate(dict->Capacity()); |
| + if (obj->IsFailure()) return obj; |
| + Dictionary* new_dict = Dictionary::cast(obj); |
| + |
| + AssertNoAllocation no_alloc; |
| + |
| + // Loose all details on properties when moving them around. |
| + // Elements do not have special details like properties. |
| + PropertyDetails no_details = PropertyDetails(NONE, NORMAL); |
| + |
| + uint32_t pos = 0; |
| + uint32_t undefs = 0; |
| + for (int i = 0; i < capacity; i++) { |
| + Object* k = dict->KeyAt(i); |
| + if (dict->IsKey(k)) { |
| + ASSERT(k->IsNumber()); |
| + ASSERT(!k->IsSmi() || Smi::cast(k)->value() >= 0); |
| + ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() >= 0); |
| + ASSERT(!k->IsHeapNumber() || HeapNumber::cast(k)->value() <= kMaxUInt32); |
| + Object* value = dict->ValueAt(i); |
| + uint32_t key = NumberToUint32(k); |
| + if (key < limit) { |
| + if (value->IsUndefined()) { |
| + undefs++; |
| + } else { |
| + new_dict->AddNumberEntry(pos, value, no_details); |
| + pos++; |
| + } |
| + } else { |
| + new_dict->AddNumberEntry(key, value, no_details); |
| + } |
| + } |
| + } |
| + |
| + uint32_t result = pos; |
| + while (undefs > 0) { |
| + new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details); |
| + pos++; |
| + undefs--; |
| + } |
| + |
| + set_elements(new_dict); |
| + |
| + if (result <= static_cast<uint32_t>(Smi::kMaxValue)) { |
| + return Smi::FromInt(static_cast<int>(result)); |
| + } |
| + result_double->set_value(static_cast<double>(result)); |
| + return result_double; |
| +} |
| + |
| + |
| +// Collects all defined (non-hole) and non-undefined (array) elements at |
| +// the start of the elements array. |
| +// If the object is in dictionary mode, it is converted to fast elements |
| +// mode. |
| +Object* JSObject::PrepareElementsForSort(uint32_t limit) { |
| + if (!HasFastElements()) { |
| + // Convert to fast elements containing only the existing properties. |
| + // Ordering is irrelevant, since we are going to sort anyway. |
| + Dictionary* dict = element_dictionary(); |
| + if (dict->requires_slow_elements() || dict->max_number_key() >= limit) { |
| + return PrepareSlowElementsForSort(limit); |
| + } |
| + // Convert to fast elements. |
| + |
| + PretenureFlag tenure = Heap::InNewSpace(this) ? NOT_TENURED: TENURED; |
| + Object* new_array = |
| + Heap::AllocateFixedArray(dict->NumberOfElements(), tenure); |
| + if (new_array->IsFailure()) { |
| + return new_array; |
| + } |
| + FixedArray* fast_elements = FixedArray::cast(new_array); |
| + dict->CopyValuesTo(fast_elements); |
| + set_elements(fast_elements); |
| + } |
| + ASSERT(HasFastElements()); |
| + |
| + // Collect holes at the end, undefined before that and the rest at the |
| + // start, and return the number of non-hole, non-undefined values. |
| + |
| + FixedArray* elements = this->elements(); |
| + uint32_t elements_length = static_cast<uint32_t>(elements->length()); |
| + if (limit > elements_length ) { |
| + limit = elements_length ; |
|
Erik Corry
2009/04/27 09:42:13
Do we have a test that excercises this?
|
| + } |
| + if (limit == 0) { |
| + return Smi::FromInt(0); |
| + } |
| + |
| + HeapNumber* result_double; |
|
Erik Corry
2009/04/27 09:42:13
And here.
|
| + if (limit > static_cast<uint32_t>(Smi::kMaxValue)) { |
| + // Pessimistically allocate space for return value before |
| + // we start mutating the array. |
| + Object* new_double = Heap::AllocateHeapNumber(0.0); |
| + if (new_double->IsFailure()) return new_double; |
| + result_double = HeapNumber::cast(new_double); |
| + } |
| + |
| + AssertNoAllocation no_alloc; |
| + |
| + // Split elements into defined, undefined and the_hole, in that order. |
| + // Only count locations for undefined and the hole, and fill them afterwards. |
| + WriteBarrierMode write_barrier = elements->GetWriteBarrierMode(); |
| + unsigned int undefs = limit; |
| + unsigned int holes = limit; |
| + // Assume most arrays contain no holes and undefined values, so minimize the |
| + // number of stores of non-undefined, non-the-hole values. |
| + for(unsigned int i = 0; i < undefs; i++) { |
| + Object* current = elements->get(i); |
| + if (current->IsTheHole()) { |
| + holes--; |
| + undefs--; |
| + } else if (current->IsUndefined()) { |
| + undefs--; |
| + } else { |
| + continue; |
| + } |
| + // Position i needs to be filled. |
| + while (undefs > i) { |
| + current = elements->get(undefs); |
| + if (current->IsTheHole()) { |
| + holes--; |
| + undefs--; |
| + } else if (current->IsUndefined()) { |
| + undefs--; |
| + } else { |
| + elements->set(i, current, write_barrier); |
| + break; |
| + } |
| + } |
| + } |
| + uint32_t result = undefs; |
| + while(undefs < holes) { |
| + elements->set_undefined(undefs); |
| + undefs++; |
| + } |
| + while (holes < limit) { |
| + elements->set_the_hole(holes); |
| + holes++; |
| + } |
| + |
| + if (result <= static_cast<uint32_t>(Smi::kMaxValue)) { |
| + return Smi::FromInt(static_cast<int>(result)); |
| + } |
| + result_double->set_value(static_cast<double>(result)); |
| + return result_double; |
| +} |
| + |
| + |
| Object* SymbolTable::LookupString(String* string, Object** s) { |
| SymbolKey key(string); |
| return LookupKey(&key, s); |