Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(876)

Unified Diff: src/objects.cc

Issue 1194023004: Let AddDictionaryElement / AddFastElement purely add, move transition heuristics to AddDataElement (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Add test Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/objects.h ('k') | test/mjsunit/arguments.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/objects.h ('k') | test/mjsunit/arguments.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698