Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index 8e36ae5337a8da2f0aea27b5b9ae1d1af2698899..f0224d217b992f5bf07d2623c0a04c9098133928 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -2896,9 +2896,12 @@ MaybeObject* JSObject::NormalizeElements() { |
int length = IsJSArray() |
? Smi::cast(JSArray::cast(this)->length())->value() |
: array->length(); |
+ int old_capacity = 0; |
+ int used_elements = 0; |
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements); |
NumberDictionary* dictionary = NULL; |
{ Object* object; |
- MaybeObject* maybe = NumberDictionary::Allocate(length); |
+ MaybeObject* maybe = NumberDictionary::Allocate(used_elements); |
if (!maybe->ToObject(&object)) return maybe; |
dictionary = NumberDictionary::cast(object); |
} |
@@ -8615,7 +8618,7 @@ MaybeObject* JSObject::SetDictionaryElement(uint32_t index, |
} else { |
new_length = dictionary->max_number_key() + 1; |
} |
- MaybeObject* result = ShouldConvertToFastDoubleElements() |
+ MaybeObject* result = CanConvertToFastDoubleElements() |
? SetFastDoubleElementsCapacityAndLength(new_length, new_length) |
: SetFastElementsCapacityAndLength(new_length, new_length); |
if (result->IsFailure()) return result; |
@@ -9157,7 +9160,15 @@ MaybeObject* JSObject::GetExternalElement(uint32_t index) { |
bool JSObject::HasDenseElements() { |
int capacity = 0; |
- int number_of_elements = 0; |
+ int used = 0; |
+ GetElementsCapacityAndUsage(&capacity, &used); |
+ return (capacity == 0) || (used > (capacity / 2)); |
+} |
+ |
+ |
+void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { |
+ *capacity = 0; |
+ *used = 0; |
FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements()); |
FixedArray* backing_store = NULL; |
@@ -9168,34 +9179,33 @@ bool JSObject::HasDenseElements() { |
backing_store = FixedArray::cast(backing_store_base); |
if (backing_store->IsDictionary()) { |
NumberDictionary* dictionary = NumberDictionary::cast(backing_store); |
- capacity = dictionary->Capacity(); |
- number_of_elements = dictionary->NumberOfElements(); |
+ *capacity = dictionary->Capacity(); |
+ *used = dictionary->NumberOfElements(); |
break; |
} |
// Fall through. |
case FAST_ELEMENTS: |
backing_store = FixedArray::cast(backing_store_base); |
- capacity = backing_store->length(); |
- for (int i = 0; i < capacity; ++i) { |
- if (!backing_store->get(i)->IsTheHole()) ++number_of_elements; |
+ *capacity = backing_store->length(); |
+ for (int i = 0; i < *capacity; ++i) { |
+ if (!backing_store->get(i)->IsTheHole()) ++(*used); |
} |
break; |
case DICTIONARY_ELEMENTS: { |
NumberDictionary* dictionary = |
NumberDictionary::cast(FixedArray::cast(elements())); |
- capacity = dictionary->Capacity(); |
- number_of_elements = dictionary->NumberOfElements(); |
+ *capacity = dictionary->Capacity(); |
+ *used = dictionary->NumberOfElements(); |
break; |
} |
case FAST_DOUBLE_ELEMENTS: { |
FixedDoubleArray* elms = FixedDoubleArray::cast(elements()); |
- capacity = elms->length(); |
- for (int i = 0; i < capacity; i++) { |
- if (!elms->is_the_hole(i)) number_of_elements++; |
+ *capacity = elms->length(); |
+ for (int i = 0; i < *capacity; i++) { |
+ if (!elms->is_the_hole(i)) ++(*used); |
} |
break; |
} |
- case EXTERNAL_PIXEL_ELEMENTS: |
case EXTERNAL_BYTE_ELEMENTS: |
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: |
case EXTERNAL_SHORT_ELEMENTS: |
@@ -9203,31 +9213,34 @@ bool JSObject::HasDenseElements() { |
case EXTERNAL_INT_ELEMENTS: |
case EXTERNAL_UNSIGNED_INT_ELEMENTS: |
case EXTERNAL_FLOAT_ELEMENTS: |
- case EXTERNAL_DOUBLE_ELEMENTS: { |
- return true; |
- } |
+ case EXTERNAL_DOUBLE_ELEMENTS: |
+ case EXTERNAL_PIXEL_ELEMENTS: |
+ // External arrays are considered 100% used. |
+ ExternalArray* external_array = ExternalArray::cast(elements()); |
+ *capacity = external_array->length(); |
+ *used = external_array->length(); |
+ break; |
} |
- return (capacity == 0) || (number_of_elements > (capacity / 2)); |
} |
bool JSObject::ShouldConvertToSlowElements(int new_capacity) { |
- if (new_capacity <= kMaxFastElementsLength) return false; |
- // Keep the array in fast case if the current backing storage is |
- // almost filled and if the new capacity is no more than twice the |
- // old capacity. |
- int old_capacity = 0; |
- if (elements()->map() == GetHeap()->non_strict_arguments_elements_map()) { |
- FixedArray* backing_store = FixedArray::cast(elements()); |
- old_capacity = FixedArray::cast(backing_store->get(1))->length(); |
- } else if (HasFastElements()) { |
- old_capacity = FixedArray::cast(elements())->length(); |
- } else if (HasFastDoubleElements()) { |
- old_capacity = FixedDoubleArray::cast(elements())->length(); |
- } else { |
- UNREACHABLE(); |
+ STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <= |
+ kMaxUncheckedFastElementsLength); |
+ if (new_capacity <= kMaxUncheckedOldFastElementsLength || |
+ (new_capacity <= kMaxUncheckedFastElementsLength && |
+ GetHeap()->InNewSpace(this))) { |
+ return false; |
} |
- return !HasDenseElements() || ((new_capacity / 2) > old_capacity); |
+ // If the fast-case backing storage takes up roughly three times as |
+ // much space (in machine words) as a dictionary backing storage |
+ // would, the object should have slow elements. |
+ int old_capacity = 0; |
+ int used_elements = 0; |
+ GetElementsCapacityAndUsage(&old_capacity, &used_elements); |
+ int dictionary_size = NumberDictionary::ComputeCapacity(used_elements) * |
+ NumberDictionary::kEntrySize; |
+ return 3 * dictionary_size <= new_capacity; |
} |
@@ -9250,20 +9263,21 @@ bool JSObject::ShouldConvertToFastElements() { |
// dictionary, we cannot go back to fast case. |
if (dictionary->requires_slow_elements()) return false; |
// If the dictionary backing storage takes up roughly half as much |
- // space as a fast-case backing storage would the array should have |
- // fast elements. |
- uint32_t length = 0; |
+ // space (in machine words) as a fast-case backing storage would, |
+ // the object should have fast elements. |
+ uint32_t array_size = 0; |
if (IsJSArray()) { |
- CHECK(JSArray::cast(this)->length()->ToArrayIndex(&length)); |
+ CHECK(JSArray::cast(this)->length()->ToArrayIndex(&array_size)); |
} else { |
- length = dictionary->max_number_key(); |
+ array_size = dictionary->max_number_key(); |
} |
- return static_cast<uint32_t>(dictionary->Capacity()) >= |
- (length / (2 * NumberDictionary::kEntrySize)); |
+ uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) * |
+ NumberDictionary::kEntrySize; |
+ return 2 * dictionary_size >= array_size; |
} |
-bool JSObject::ShouldConvertToFastDoubleElements() { |
+bool JSObject::CanConvertToFastDoubleElements() { |
if (FLAG_unbox_double_arrays) { |
ASSERT(HasDictionaryElements()); |
NumberDictionary* dictionary = NumberDictionary::cast(elements()); |
@@ -10194,11 +10208,8 @@ void HashTable<Shape, Key>::IterateElements(ObjectVisitor* v) { |
template<typename Shape, typename Key> |
MaybeObject* HashTable<Shape, Key>::Allocate(int at_least_space_for, |
PretenureFlag pretenure) { |
- const int kMinCapacity = 32; |
- int capacity = RoundUpToPowerOf2(at_least_space_for * 2); |
- if (capacity < kMinCapacity) { |
- capacity = kMinCapacity; // Guarantee min capacity. |
- } else if (capacity > HashTable::kMaxCapacity) { |
+ int capacity = ComputeCapacity(at_least_space_for); |
+ if (capacity > HashTable::kMaxCapacity) { |
return Failure::OutOfMemoryException(); |
} |