| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index dde6179007ef8519e4c02f87b768a0037d539d67..7a347d9d3fe65f0e8ef378cb39a4f2d85fe31ef4 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -12285,6 +12285,31 @@ void JSObject::ValidateElements(Handle<JSObject> object) {
|
| }
|
|
|
|
|
| +static void AddDictionaryElement(Handle<JSObject> object,
|
| + Handle<SeededNumberDictionary> dictionary,
|
| + uint32_t index, Handle<Object> value,
|
| + PropertyAttributes attributes) {
|
| +// TODO(verwaest): Handle with the elements accessor.
|
| +// Insert element in the dictionary.
|
| +#ifdef DEBUG
|
| + int entry = dictionary->FindEntry(index);
|
| + DCHECK_EQ(SeededNumberDictionary::kNotFound, entry);
|
| +#endif
|
| +
|
| + PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
|
| + Handle<SeededNumberDictionary> new_dictionary =
|
| + SeededNumberDictionary::AddNumberEntry(dictionary, index, value, details);
|
| +
|
| + if (*dictionary == *new_dictionary) return;
|
| +
|
| + if (object->HasSloppyArgumentsElements()) {
|
| + FixedArray::cast(object->elements())->set(1, *new_dictionary);
|
| + } else {
|
| + object->set_elements(*new_dictionary);
|
| + }
|
| +}
|
| +
|
| +
|
| void JSObject::SetDictionaryArgumentsElement(Handle<JSObject> object,
|
| uint32_t index,
|
| Handle<Object> value,
|
| @@ -12314,7 +12339,9 @@ void JSObject::SetDictionaryArgumentsElement(Handle<JSObject> object,
|
| if ((attributes & READ_ONLY) == 0) {
|
| value = isolate->factory()->NewAliasedArgumentsEntry(context_index);
|
| }
|
| - AddDictionaryElement(object, index, value, attributes);
|
| + Handle<SeededNumberDictionary> dictionary(
|
| + SeededNumberDictionary::cast(parameter_map->get(1)));
|
| + AddDictionaryElement(object, dictionary, index, value, attributes);
|
| } else {
|
| SetDictionaryElement(object, index, value, attributes);
|
| }
|
| @@ -12366,79 +12393,6 @@ 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,
|
| - Handle<Object> value,
|
| - PropertyAttributes attributes) {
|
| - // TODO(verwaest): Handle with the elements accessor.
|
| - // Insert element in the dictionary.
|
| - DCHECK(object->HasDictionaryElements() ||
|
| - object->HasDictionaryArgumentsElements());
|
| - DCHECK(object->map()->is_extensible());
|
| -
|
| - Handle<FixedArray> elements(FixedArray::cast(object->elements()));
|
| - bool is_arguments = object->HasSloppyArgumentsElements();
|
| - Handle<SeededNumberDictionary> dictionary(
|
| - is_arguments ? SeededNumberDictionary::cast(elements->get(1))
|
| - : SeededNumberDictionary::cast(*elements));
|
| -
|
| -#ifdef DEBUG
|
| - int entry = dictionary->FindEntry(index);
|
| - DCHECK_EQ(SeededNumberDictionary::kNotFound, entry);
|
| -#endif
|
| -
|
| - PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
|
| - Handle<SeededNumberDictionary> new_dictionary =
|
| - SeededNumberDictionary::AddNumberEntry(dictionary, index, value, details);
|
| -
|
| - if (*dictionary != *new_dictionary) {
|
| - if (is_arguments) {
|
| - elements->set(1, *new_dictionary);
|
| - } else {
|
| - object->set_elements(*new_dictionary);
|
| - }
|
| - }
|
| -
|
| - uint32_t length = 0;
|
| - if (object->IsJSArray()) {
|
| - CHECK(JSArray::cast(*object)->length()->ToArrayLength(&length));
|
| - 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.
|
| - if (object->HasDenseElements() &&
|
| - ShouldConvertToFastElements(*new_dictionary, length)) {
|
| - ElementsKind to_kind = object->BestFittingFastElementsKind();
|
| - ElementsAccessor* accessor = ElementsAccessor::ForKind(to_kind);
|
| - accessor->GrowCapacityAndConvert(object, length);
|
| -#ifdef DEBUG
|
| - if (FLAG_trace_normalization) {
|
| - OFStream os(stdout);
|
| - os << "Object elements are fast case again:\n";
|
| - object->Print(os);
|
| - }
|
| -#endif
|
| - }
|
| -}
|
| -
|
| -
|
| // static
|
| MaybeHandle<Object> JSReceiver::SetElement(Handle<JSReceiver> object,
|
| uint32_t index, Handle<Object> value,
|
| @@ -12451,16 +12405,9 @@ MaybeHandle<Object> JSReceiver::SetElement(Handle<JSReceiver> object,
|
|
|
| static void AddFastElement(Handle<JSObject> object, uint32_t index,
|
| Handle<Object> value, ElementsKind from_kind,
|
| - uint32_t capacity, uint32_t new_capacity) {
|
| - // Check if the length property of this object needs to be updated.
|
| - uint32_t array_length = 0;
|
| - bool introduces_holes = true;
|
| - if (object->IsJSArray()) {
|
| - CHECK(JSArray::cast(*object)->length()->ToArrayLength(&array_length));
|
| - introduces_holes = index > array_length;
|
| - } else {
|
| - introduces_holes = index >= capacity;
|
| - }
|
| + uint32_t array_length, uint32_t capacity,
|
| + uint32_t new_capacity) {
|
| + bool introduces_holes = !object->IsJSArray() || index > array_length;
|
|
|
| ElementsKind to_kind = value->OptimalElementsKind();
|
| if (IsHoleyElementsKind(from_kind)) to_kind = GetHoleyElementsKind(to_kind);
|
| @@ -12470,7 +12417,9 @@ static void AddFastElement(Handle<JSObject> object, uint32_t index,
|
| ElementsAccessor* accessor = ElementsAccessor::ForKind(to_kind);
|
|
|
| // Increase backing store capacity if that's been decided previously.
|
| - if (capacity != new_capacity || IsDictionaryElementsKind(from_kind) ||
|
| + // The capacity is indicated as 0 if the incoming object was dictionary or
|
| + // slow-mode sloppy arguments.
|
| + if (capacity != new_capacity ||
|
| IsFastDoubleElementsKind(from_kind) !=
|
| IsFastDoubleElementsKind(to_kind)) {
|
| accessor->GrowCapacityAndConvert(object, new_capacity);
|
| @@ -12478,11 +12427,95 @@ static void AddFastElement(Handle<JSObject> object, uint32_t index,
|
| JSObject::TransitionElementsKind(object, to_kind);
|
| }
|
|
|
| - if (object->IsJSArray() && index >= array_length) {
|
| - Handle<JSArray>::cast(object)->set_length(Smi::FromInt(index + 1));
|
| + accessor->Set(object->elements(), index, *value);
|
| +}
|
| +
|
| +
|
| +// Do we want to keep fast elements when adding an element at |index|? Returns
|
| +// |new_capacity| indicating to which capacity the object should be increased.
|
| +static bool ShouldConvertToSlowElements(JSObject* object, uint32_t capacity,
|
| + uint32_t index,
|
| + uint32_t* new_capacity) {
|
| + STATIC_ASSERT(JSObject::kMaxUncheckedOldFastElementsLength <=
|
| + JSObject::kMaxUncheckedFastElementsLength);
|
| + if (index < capacity) {
|
| + *new_capacity = capacity;
|
| + return false;
|
| + }
|
| + if (index - capacity >= JSObject::kMaxGap) return true;
|
| + *new_capacity = JSObject::NewElementsCapacity(index + 1);
|
| + DCHECK_LT(index, *new_capacity);
|
| + if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength ||
|
| + (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength &&
|
| + object->GetHeap()->InNewSpace(object))) {
|
| + return false;
|
| + }
|
| + // 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;
|
| + object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
|
| + int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
|
| + SeededNumberDictionary::kEntrySize;
|
| + return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity;
|
| +}
|
| +
|
| +
|
| +bool JSObject::WouldConvertToSlowElements(uint32_t index) {
|
| + if (HasFastElements()) {
|
| + Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
|
| + uint32_t capacity = static_cast<uint32_t>(backing_store->length());
|
| + uint32_t new_capacity;
|
| + return ShouldConvertToSlowElements(this, capacity, index, &new_capacity);
|
| }
|
| + return false;
|
| +}
|
|
|
| - accessor->Set(object->elements(), index, *value);
|
| +
|
| +static ElementsKind BestFittingFastElementsKind(JSObject* object) {
|
| + if (object->HasSloppyArgumentsElements()) return SLOPPY_ARGUMENTS_ELEMENTS;
|
| + DCHECK(object->HasDictionaryElements());
|
| + SeededNumberDictionary* dictionary = object->element_dictionary();
|
| + ElementsKind kind = FAST_HOLEY_SMI_ELEMENTS;
|
| + for (int i = 0; i < dictionary->Capacity(); i++) {
|
| + Object* key = dictionary->KeyAt(i);
|
| + if (key->IsNumber()) {
|
| + Object* value = dictionary->ValueAt(i);
|
| + if (!value->IsNumber()) return FAST_HOLEY_ELEMENTS;
|
| + if (!value->IsSmi()) {
|
| + if (!FLAG_unbox_double_arrays) return FAST_HOLEY_ELEMENTS;
|
| + kind = FAST_HOLEY_DOUBLE_ELEMENTS;
|
| + }
|
| + }
|
| + }
|
| + return kind;
|
| +}
|
| +
|
| +
|
| +static bool ShouldConvertToFastElements(JSObject* object,
|
| + SeededNumberDictionary* dictionary,
|
| + uint32_t index,
|
| + uint32_t* new_capacity) {
|
| + // If properties with non-standard attributes or accessors were added, we
|
| + // cannot go back to fast elements.
|
| + if (dictionary->requires_slow_elements()) return false;
|
| +
|
| + // Adding a property with this index will require slow elements.
|
| + if (index >= static_cast<uint32_t>(Smi::kMaxValue)) return false;
|
| +
|
| + if (object->IsJSArray()) {
|
| + Object* length = JSArray::cast(object)->length();
|
| + if (!length->IsSmi()) return false;
|
| + *new_capacity = static_cast<uint32_t>(Smi::cast(length)->value());
|
| + } else {
|
| + *new_capacity = dictionary->max_number_key() + 1;
|
| + }
|
| + *new_capacity = Max(index + 1, *new_capacity);
|
| +
|
| + uint32_t dictionary_size = static_cast<uint32_t>(dictionary->Capacity()) *
|
| + SeededNumberDictionary::kEntrySize;
|
| + return 2 * dictionary_size >= *new_capacity;
|
| }
|
|
|
|
|
| @@ -12495,61 +12528,67 @@ MaybeHandle<Object> JSObject::AddDataElement(Handle<JSObject> object,
|
|
|
| Isolate* isolate = object->GetIsolate();
|
|
|
| - // TODO(verwaest): Use ElementAccessor.
|
| - Handle<Object> old_length_handle;
|
| - if (object->IsJSArray() && object->map()->is_observed()) {
|
| - old_length_handle = handle(JSArray::cast(*object)->length(), isolate);
|
| - }
|
| -
|
| ElementsKind kind = object->GetElementsKind();
|
| - bool handle_slow = IsDictionaryElementsKind(kind);
|
| - uint32_t capacity = 0;
|
| + bool handle_slow;
|
| + uint32_t old_length = 0;
|
| + uint32_t old_capacity = 0;
|
| uint32_t new_capacity = 0;
|
| - if (attributes != NONE) {
|
| - // TODO(verwaest): Move set_requires_slow_elements into NormalizeElements.
|
| - NormalizeElements(object)->set_requires_slow_elements();
|
| - handle_slow = true;
|
| - } else if (IsSloppyArgumentsElements(kind)) {
|
| - FixedArray* parameter_map = FixedArray::cast(object->elements());
|
| - FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
|
| - if (arguments->IsDictionary()) {
|
| - handle_slow = true;
|
| - } else {
|
| - capacity = static_cast<uint32_t>(arguments->length());
|
| - handle_slow =
|
| - object->ShouldConvertToSlowElements(capacity, index, &new_capacity);
|
| - if (handle_slow) NormalizeElements(object);
|
| +
|
| + Handle<Object> old_length_handle;
|
| + if (object->IsJSArray()) {
|
| + CHECK(JSArray::cast(*object)->length()->ToArrayLength(&old_length));
|
| + if (object->map()->is_observed()) {
|
| + old_length_handle = handle(JSArray::cast(*object)->length(), isolate);
|
| }
|
| - } else if (!handle_slow) {
|
| - capacity = static_cast<uint32_t>(object->elements()->length());
|
| + }
|
| +
|
| + Handle<SeededNumberDictionary> dictionary;
|
| + FixedArrayBase* elements = object->elements();
|
| + if (IsSloppyArgumentsElements(kind)) {
|
| + elements = FixedArrayBase::cast(FixedArray::cast(elements)->get(1));
|
| + }
|
| +
|
| + if (elements->IsSeededNumberDictionary()) {
|
| + dictionary = handle(SeededNumberDictionary::cast(elements));
|
| + handle_slow = attributes != NONE ||
|
| + !ShouldConvertToFastElements(*object, *dictionary, index,
|
| + &new_capacity);
|
| + if (!handle_slow) kind = BestFittingFastElementsKind(*object);
|
| + } else {
|
| + old_capacity = static_cast<uint32_t>(elements->length());
|
| handle_slow =
|
| - object->ShouldConvertToSlowElements(capacity, index, &new_capacity);
|
| + attributes != NONE || ShouldConvertToSlowElements(*object, old_capacity,
|
| + index, &new_capacity);
|
| if (handle_slow) {
|
| - NormalizeElements(object);
|
| + dictionary = NormalizeElements(object);
|
| } else if (IsFastSmiOrObjectElementsKind(kind)) {
|
| EnsureWritableFastElements(object);
|
| }
|
| }
|
|
|
| if (handle_slow) {
|
| + if (attributes != NONE) dictionary->set_requires_slow_elements();
|
| DCHECK(object->HasDictionaryElements() ||
|
| object->HasDictionaryArgumentsElements());
|
| - AddDictionaryElement(object, index, value, attributes);
|
| + AddDictionaryElement(object, dictionary, index, value, attributes);
|
| } else {
|
| - AddFastElement(object, index, value, kind, capacity, new_capacity);
|
| + AddFastElement(object, index, value, kind, old_length, old_capacity,
|
| + new_capacity);
|
| + }
|
| +
|
| + uint32_t new_length = old_length;
|
| + Handle<Object> new_length_handle;
|
| + if (object->IsJSArray() && index >= old_length) {
|
| + new_length = index + 1;
|
| + new_length_handle = isolate->factory()->NewNumberFromUint(new_length);
|
| + JSArray::cast(*object)->set_length(*new_length_handle);
|
| }
|
|
|
| - if (!old_length_handle.is_null() &&
|
| - !old_length_handle->SameValue(Handle<JSArray>::cast(object)->length())) {
|
| + if (!old_length_handle.is_null() && new_length != old_length) {
|
| // |old_length_handle| is kept null above unless the object is observed.
|
| DCHECK(object->map()->is_observed());
|
| Handle<JSArray> array = Handle<JSArray>::cast(object);
|
| Handle<String> name = isolate->factory()->Uint32ToString(index);
|
| - Handle<Object> new_length_handle(array->length(), isolate);
|
| - uint32_t old_length = 0;
|
| - uint32_t new_length = 0;
|
| - CHECK(old_length_handle->ToArrayLength(&old_length));
|
| - CHECK(new_length_handle->ToArrayLength(&new_length));
|
|
|
| RETURN_ON_EXCEPTION(isolate, BeginPerformSplice(array), Object);
|
| RETURN_ON_EXCEPTION(
|
| @@ -12777,14 +12816,6 @@ MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) {
|
| }
|
|
|
|
|
| -bool JSObject::HasDenseElements() {
|
| - int capacity = 0;
|
| - int used = 0;
|
| - GetElementsCapacityAndUsage(&capacity, &used);
|
| - return (capacity == 0) || (used > (capacity / 2));
|
| -}
|
| -
|
| -
|
| void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
|
| *capacity = 0;
|
| *used = 0;
|
| @@ -12860,65 +12891,6 @@ void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
|
| }
|
|
|
|
|
| -bool JSObject::WouldConvertToSlowElements(uint32_t index) {
|
| - if (HasFastElements()) {
|
| - Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
|
| - uint32_t capacity = static_cast<uint32_t>(backing_store->length());
|
| - uint32_t new_capacity;
|
| - return ShouldConvertToSlowElements(capacity, index, &new_capacity);
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -
|
| -bool JSObject::ShouldConvertToSlowElements(uint32_t capacity, uint32_t index,
|
| - uint32_t* new_capacity) {
|
| - STATIC_ASSERT(kMaxUncheckedOldFastElementsLength <=
|
| - kMaxUncheckedFastElementsLength);
|
| - if (index < capacity) {
|
| - *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))) {
|
| - return false;
|
| - }
|
| - // 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 = SeededNumberDictionary::ComputeCapacity(used_elements) *
|
| - SeededNumberDictionary::kEntrySize;
|
| - return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity;
|
| -}
|
| -
|
| -
|
| -ElementsKind JSObject::BestFittingFastElementsKind() {
|
| - if (HasSloppyArgumentsElements()) return FAST_HOLEY_ELEMENTS;
|
| - DCHECK(HasDictionaryElements());
|
| - SeededNumberDictionary* dictionary = element_dictionary();
|
| - ElementsKind kind = FAST_HOLEY_SMI_ELEMENTS;
|
| - for (int i = 0; i < dictionary->Capacity(); i++) {
|
| - Object* key = dictionary->KeyAt(i);
|
| - if (key->IsNumber()) {
|
| - Object* value = dictionary->ValueAt(i);
|
| - if (!value->IsNumber()) return FAST_HOLEY_ELEMENTS;
|
| - if (!value->IsSmi()) {
|
| - if (!FLAG_unbox_double_arrays) return FAST_HOLEY_ELEMENTS;
|
| - kind = FAST_HOLEY_DOUBLE_ELEMENTS;
|
| - }
|
| - }
|
| - }
|
| - return kind;
|
| -}
|
| -
|
| -
|
| // Certain compilers request function template instantiation when they
|
| // see the definition of the other template functions in the
|
| // class. This requires us to have the template functions put
|
|
|