Commit dee4895d authored by verwaest's avatar verwaest Committed by Commit bot

Cleanup adding elements and in particular dictionary elements

BUG=v8:4137
LOG=n

Review URL: https://codereview.chromium.org/1196163005

Cr-Commit-Position: refs/heads/master@{#29232}
parent 7f5a2d9e
...@@ -989,12 +989,13 @@ class FastElementsAccessor ...@@ -989,12 +989,13 @@ class FastElementsAccessor
if (length == 0) return; // nothing to do! if (length == 0) return; // nothing to do!
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
Handle<BackingStore> backing_store = Handle<BackingStore>::cast(elements); Handle<BackingStore> backing_store = Handle<BackingStore>::cast(elements);
if (IsFastSmiElementsKind(KindTraits::Kind)) {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
DCHECK((!IsFastSmiElementsKind(KindTraits::Kind) || DCHECK(BackingStore::get(backing_store, i)->IsSmi() ||
BackingStore::get(backing_store, i)->IsSmi()) || (IsFastHoleyElementsKind(KindTraits::Kind) &&
(IsFastHoleyElementsKind(KindTraits::Kind) ==
backing_store->is_the_hole(i))); backing_store->is_the_hole(i)));
} }
}
#endif #endif
} }
}; };
...@@ -1345,13 +1346,9 @@ class DictionaryElementsAccessor ...@@ -1345,13 +1346,9 @@ class DictionaryElementsAccessor
dict->ElementsRemoved(removed_entries); dict->ElementsRemoved(removed_entries);
} }
if (length <= Smi::kMaxValue) {
array->set_length(Smi::FromInt(length));
} else {
Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length); Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length);
array->set_length(*length_obj); array->set_length(*length_obj);
} }
}
static void DeleteCommon(Handle<JSObject> obj, uint32_t key, static void DeleteCommon(Handle<JSObject> obj, uint32_t key,
LanguageMode language_mode) { LanguageMode language_mode) {
......
...@@ -2913,6 +2913,7 @@ bool SeededNumberDictionary::requires_slow_elements() { ...@@ -2913,6 +2913,7 @@ bool SeededNumberDictionary::requires_slow_elements() {
(Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask); (Smi::cast(max_index_object)->value() & kRequiresSlowElementsMask);
} }
uint32_t SeededNumberDictionary::max_number_key() { uint32_t SeededNumberDictionary::max_number_key() {
DCHECK(!requires_slow_elements()); DCHECK(!requires_slow_elements());
Object* max_index_object = get(kMaxNumberKeyIndex); Object* max_index_object = get(kMaxNumberKeyIndex);
...@@ -2921,6 +2922,7 @@ uint32_t SeededNumberDictionary::max_number_key() { ...@@ -2921,6 +2922,7 @@ uint32_t SeededNumberDictionary::max_number_key() {
return value >> kRequiresSlowElementsTagSize; return value >> kRequiresSlowElementsTagSize;
} }
void SeededNumberDictionary::set_requires_slow_elements() { void SeededNumberDictionary::set_requires_slow_elements() {
set(kMaxNumberKeyIndex, Smi::FromInt(kRequiresSlowElementsMask)); set(kMaxNumberKeyIndex, Smi::FromInt(kRequiresSlowElementsMask));
} }
......
...@@ -12447,20 +12447,28 @@ void JSObject::SetDictionaryElement(Handle<JSObject> object, uint32_t index, ...@@ -12447,20 +12447,28 @@ void JSObject::SetDictionaryElement(Handle<JSObject> object, uint32_t index,
} }
static bool ShouldConvertToFastElements(SeededNumberDictionary* dictionary,
uint32_t array_size) {
// If properties with non-standard attributes or accessors were added, we
// cannot go back to fast elements.
if (dictionary->requires_slow_elements()) return false;
uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
SeededNumberDictionary::kEntrySize;
return 2 * dictionary_size >= array_size;
}
void JSObject::AddDictionaryElement(Handle<JSObject> object, uint32_t index, void JSObject::AddDictionaryElement(Handle<JSObject> object, uint32_t index,
Handle<Object> value, Handle<Object> value,
PropertyAttributes attributes) { PropertyAttributes attributes) {
// TODO(verwaest): Handle with the elements accessor. // TODO(verwaest): Handle with the elements accessor.
Isolate* isolate = object->GetIsolate();
// Insert element in the dictionary. // Insert element in the dictionary.
Handle<FixedArray> elements(FixedArray::cast(object->elements()));
bool is_arguments =
(elements->map() == isolate->heap()->sloppy_arguments_elements_map());
DCHECK(object->HasDictionaryElements() || DCHECK(object->HasDictionaryElements() ||
object->HasDictionaryArgumentsElements()); object->HasDictionaryArgumentsElements());
DCHECK(object->map()->is_extensible());
Handle<FixedArray> elements(FixedArray::cast(object->elements()));
bool is_arguments = object->HasSloppyArgumentsElements();
Handle<SeededNumberDictionary> dictionary( Handle<SeededNumberDictionary> dictionary(
is_arguments ? SeededNumberDictionary::cast(elements->get(1)) is_arguments ? SeededNumberDictionary::cast(elements->get(1))
: SeededNumberDictionary::cast(*elements)); : SeededNumberDictionary::cast(*elements));
...@@ -12468,38 +12476,39 @@ void JSObject::AddDictionaryElement(Handle<JSObject> object, uint32_t index, ...@@ -12468,38 +12476,39 @@ void JSObject::AddDictionaryElement(Handle<JSObject> object, uint32_t index,
#ifdef DEBUG #ifdef DEBUG
int entry = dictionary->FindEntry(index); int entry = dictionary->FindEntry(index);
DCHECK_EQ(SeededNumberDictionary::kNotFound, entry); DCHECK_EQ(SeededNumberDictionary::kNotFound, entry);
DCHECK(object->map()->is_extensible());
#endif #endif
PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell); PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
Handle<SeededNumberDictionary> new_dictionary = Handle<SeededNumberDictionary> new_dictionary =
SeededNumberDictionary::AddNumberEntry(dictionary, index, value, details); SeededNumberDictionary::AddNumberEntry(dictionary, index, value, details);
if (*dictionary != *new_dictionary) { if (*dictionary != *new_dictionary) {
if (is_arguments) { if (is_arguments) {
elements->set(1, *new_dictionary); elements->set(1, *new_dictionary);
} else { } else {
object->set_elements(*new_dictionary); object->set_elements(*new_dictionary);
} }
dictionary = new_dictionary;
} }
// Update the array length if this JSObject is an array. uint32_t length = 0;
if (object->IsJSArray()) { if (object->IsJSArray()) {
JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray>::cast(object), index, CHECK(JSArray::cast(*object)->length()->ToArrayLength(&length));
value); if (index >= length) {
length = index + 1;
Isolate* isolate = object->GetIsolate();
Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length);
JSArray::cast(*object)->set_length(*length_obj);
}
} else if (!new_dictionary->requires_slow_elements()) {
length = new_dictionary->max_number_key() + 1;
} }
// Attempt to put this object back in fast case. // Attempt to put this object back in fast case.
if (object->ShouldConvertToFastElements()) { if (object->HasDenseElements() &&
uint32_t new_length = 0; ShouldConvertToFastElements(*new_dictionary, length)) {
if (object->IsJSArray()) {
CHECK(JSArray::cast(*object)->length()->ToArrayLength(&new_length));
} else {
new_length = dictionary->max_number_key() + 1;
}
ElementsKind to_kind = object->BestFittingFastElementsKind(); ElementsKind to_kind = object->BestFittingFastElementsKind();
ElementsAccessor* accessor = ElementsAccessor::ForKind(to_kind); ElementsAccessor* accessor = ElementsAccessor::ForKind(to_kind);
accessor->GrowCapacityAndConvert(object, new_length); accessor->GrowCapacityAndConvert(object, length);
#ifdef DEBUG #ifdef DEBUG
if (FLAG_trace_normalization) { if (FLAG_trace_normalization) {
OFStream os(stdout); OFStream os(stdout);
...@@ -12574,41 +12583,33 @@ MaybeHandle<Object> JSObject::AddDataElement(Handle<JSObject> object, ...@@ -12574,41 +12583,33 @@ MaybeHandle<Object> JSObject::AddDataElement(Handle<JSObject> object,
} }
ElementsKind kind = object->GetElementsKind(); ElementsKind kind = object->GetElementsKind();
bool handle_slow = false; bool handle_slow = IsDictionaryElementsKind(kind);
uint32_t capacity = 0; uint32_t capacity = 0;
uint32_t new_capacity = 0; uint32_t new_capacity = 0;
if (IsFastElementsKind(kind) || object->HasFastArgumentsElements()) {
if (attributes != NONE) { if (attributes != NONE) {
// TODO(verwaest): Move set_requires_slow_elements into NormalizeElements. // TODO(verwaest): Move set_requires_slow_elements into NormalizeElements.
NormalizeElements(object)->set_requires_slow_elements(); NormalizeElements(object)->set_requires_slow_elements();
handle_slow = true; handle_slow = true;
} else { } else if (IsSloppyArgumentsElements(kind)) {
if (IsSloppyArgumentsElements(kind)) {
FixedArray* parameter_map = FixedArray::cast(object->elements()); FixedArray* parameter_map = FixedArray::cast(object->elements());
FixedArray* arguments = FixedArray::cast(parameter_map->get(1)); FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
capacity = static_cast<uint32_t>(arguments->length()); if (arguments->IsDictionary()) {
} else {
if (IsFastSmiOrObjectElementsKind(kind)) {
EnsureWritableFastElements(object);
}
capacity = static_cast<uint32_t>(object->elements()->length());
}
new_capacity = capacity;
// Check if the capacity of the backing store needs to be increased, or if
// a transition to slow elements is necessary.
if (index >= capacity) {
handle_slow = true; handle_slow = true;
if ((index - capacity) < kMaxGap) { } else {
new_capacity = NewElementsCapacity(index + 1); capacity = static_cast<uint32_t>(arguments->length());
DCHECK_LT(index, new_capacity); handle_slow =
handle_slow = object->ShouldConvertToSlowElements(new_capacity); object->ShouldConvertToSlowElements(capacity, index, &new_capacity);
}
if (handle_slow) NormalizeElements(object); if (handle_slow) NormalizeElements(object);
} }
} else if (!handle_slow) {
capacity = static_cast<uint32_t>(object->elements()->length());
handle_slow =
object->ShouldConvertToSlowElements(capacity, index, &new_capacity);
if (handle_slow) {
NormalizeElements(object);
} else if (IsFastSmiOrObjectElementsKind(kind)) {
EnsureWritableFastElements(object);
} }
} else {
handle_slow = true;
} }
if (handle_slow) { if (handle_slow) {
...@@ -12828,21 +12829,6 @@ bool Map::IsValidElementsTransition(ElementsKind from_kind, ...@@ -12828,21 +12829,6 @@ bool Map::IsValidElementsTransition(ElementsKind from_kind,
} }
void JSArray::JSArrayUpdateLengthFromIndex(Handle<JSArray> array,
uint32_t index,
Handle<Object> value) {
uint32_t old_len = 0;
CHECK(array->length()->ToArrayLength(&old_len));
// Check to see if we need to update the length. For now, we make
// sure that the length stays within 32-bits (unsigned).
if (index >= old_len && index != 0xffffffff) {
Handle<Object> len = array->GetIsolate()->factory()->NewNumber(
static_cast<double>(index) + 1);
array->set_length(*len);
}
}
bool JSArray::HasReadOnlyLength(Handle<JSArray> array) { bool JSArray::HasReadOnlyLength(Handle<JSArray> array) {
LookupIterator it(array, array->GetIsolate()->factory()->length_string(), LookupIterator it(array, array->GetIsolate()->factory()->length_string(),
LookupIterator::OWN_SKIP_INTERCEPTOR); LookupIterator::OWN_SKIP_INTERCEPTOR);
...@@ -12959,21 +12945,26 @@ bool JSObject::WouldConvertToSlowElements(uint32_t index) { ...@@ -12959,21 +12945,26 @@ bool JSObject::WouldConvertToSlowElements(uint32_t index) {
if (HasFastElements()) { if (HasFastElements()) {
Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements())); Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
uint32_t capacity = static_cast<uint32_t>(backing_store->length()); uint32_t capacity = static_cast<uint32_t>(backing_store->length());
if (index >= capacity) { uint32_t new_capacity;
if ((index - capacity) >= kMaxGap) return true; return ShouldConvertToSlowElements(capacity, index, &new_capacity);
uint32_t new_capacity = NewElementsCapacity(index + 1);
return ShouldConvertToSlowElements(new_capacity);
}
} }
return false; return false;
} }
bool JSObject::ShouldConvertToSlowElements(int new_capacity) { bool JSObject::ShouldConvertToSlowElements(uint32_t capacity, uint32_t index,
uint32_t* new_capacity) {
STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <= STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
kMaxUncheckedFastElementsLength); kMaxUncheckedFastElementsLength);
if (new_capacity <= kMaxUncheckedOldFastElementsLength || if (index < capacity) {
(new_capacity <= kMaxUncheckedFastElementsLength && *new_capacity = capacity;
return false;
}
if (index - capacity >= kMaxGap) return true;
*new_capacity = NewElementsCapacity(index + 1);
DCHECK_LT(index, *new_capacity);
if (*new_capacity <= kMaxUncheckedOldFastElementsLength ||
(*new_capacity <= kMaxUncheckedFastElementsLength &&
GetHeap()->InNewSpace(this))) { GetHeap()->InNewSpace(this))) {
return false; return false;
} }
...@@ -12985,43 +12976,7 @@ bool JSObject::ShouldConvertToSlowElements(int new_capacity) { ...@@ -12985,43 +12976,7 @@ bool JSObject::ShouldConvertToSlowElements(int new_capacity) {
GetElementsCapacityAndUsage(&old_capacity, &used_elements); GetElementsCapacityAndUsage(&old_capacity, &used_elements);
int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) * int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
SeededNumberDictionary::kEntrySize; SeededNumberDictionary::kEntrySize;
return 3 * dictionary_size <= new_capacity; return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity;
}
bool JSObject::ShouldConvertToFastElements() {
DCHECK(HasDictionaryElements() || HasDictionaryArgumentsElements());
// If the elements are sparse, we should not go back to fast case.
if (!HasDenseElements()) return false;
// An object requiring access checks is never allowed to have fast
// elements. If it had fast elements we would skip security checks.
if (IsAccessCheckNeeded()) return false;
// Observed objects may not go to fast mode because they rely on map checks,
// and for fast element accesses we sometimes check element kinds only.
if (map()->is_observed()) return false;
FixedArray* elements = FixedArray::cast(this->elements());
SeededNumberDictionary* dictionary = NULL;
if (elements->map() == GetHeap()->sloppy_arguments_elements_map()) {
dictionary = SeededNumberDictionary::cast(elements->get(1));
} else {
dictionary = SeededNumberDictionary::cast(elements);
}
// If an element has been added at a very high index in the elements
// 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 (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()->ToArrayLength(&array_size));
} else {
array_size = dictionary->max_number_key();
}
uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
SeededNumberDictionary::kEntrySize;
return 2 * dictionary_size >= array_size;
} }
......
...@@ -2015,14 +2015,11 @@ class JSObject: public JSReceiver { ...@@ -2015,14 +2015,11 @@ class JSObject: public JSReceiver {
// an access at key? // an access at key?
bool WouldConvertToSlowElements(uint32_t index); bool WouldConvertToSlowElements(uint32_t index);
inline bool WouldConvertToSlowElements(Handle<Object> key); inline bool WouldConvertToSlowElements(Handle<Object> key);
// Do we want to keep the elements in fast case when increasing the // Do we want to keep fast elements when adding an element at |index|?
// capacity? // Returns |new_capacity| indicating to which capacity the object should be
bool ShouldConvertToSlowElements(int new_capacity); // increased.
// Returns true if the backing storage for the slow-case elements of bool ShouldConvertToSlowElements(uint32_t capacity, uint32_t index,
// this object takes up nearly as much space as a fast-case backing uint32_t* new_capacity);
// storage would. In that case the JSObject should have fast
// elements.
bool ShouldConvertToFastElements();
ElementsKind BestFittingFastElementsKind(); ElementsKind BestFittingFastElementsKind();
// Computes the new capacity when expanding the elements of a JSObject. // Computes the new capacity when expanding the elements of a JSObject.
...@@ -10140,10 +10137,6 @@ class JSArray: public JSObject { ...@@ -10140,10 +10137,6 @@ class JSArray: public JSObject {
// is set to a smi. This matches the set function on FixedArray. // is set to a smi. This matches the set function on FixedArray.
inline void set_length(Smi* length); inline void set_length(Smi* length);
static void JSArrayUpdateLengthFromIndex(Handle<JSArray> array,
uint32_t index,
Handle<Object> value);
static bool HasReadOnlyLength(Handle<JSArray> array); static bool HasReadOnlyLength(Handle<JSArray> array);
static bool WouldChangeReadOnlyLength(Handle<JSArray> array, uint32_t index); static bool WouldChangeReadOnlyLength(Handle<JSArray> array, uint32_t index);
static MaybeHandle<Object> ReadOnlyLengthError(Handle<JSArray> array); static MaybeHandle<Object> ReadOnlyLengthError(Handle<JSArray> array);
......
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