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

Side by Side Diff: src/objects.cc

Issue 1241883002: Cleanup element normalization logic (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 5 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 unified diff | Download patch
« no previous file with comments | « src/objects.h ('k') | test/cctest/test-api.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 the V8 project authors. All rights reserved. 1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <iomanip> 5 #include <iomanip>
6 #include <sstream> 6 #include <sstream>
7 7
8 #include "src/v8.h" 8 #include "src/v8.h"
9 9
10 #include "src/accessors.h" 10 #include "src/accessors.h"
(...skipping 4771 matching lines...) Expand 10 before | Expand all | Expand 10 after
4782 if (!value->IsTheHole()) { 4782 if (!value->IsTheHole()) {
4783 PropertyDetails details = PropertyDetails::Empty(); 4783 PropertyDetails details = PropertyDetails::Empty();
4784 dictionary = 4784 dictionary =
4785 SeededNumberDictionary::AddNumberEntry(dictionary, i, value, details); 4785 SeededNumberDictionary::AddNumberEntry(dictionary, i, value, details);
4786 } 4786 }
4787 } 4787 }
4788 return dictionary; 4788 return dictionary;
4789 } 4789 }
4790 4790
4791 4791
4792 Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
4793 Handle<JSObject> object, Handle<FixedArrayBase> elements) {
4794 DCHECK(!object->HasDictionaryElements());
4795 DCHECK(!object->HasSlowArgumentsElements());
4796 Isolate* isolate = object->GetIsolate();
4797 // Ensure that notifications fire if the array or object prototypes are
4798 // normalizing.
4799 isolate->UpdateArrayProtectorOnNormalizeElements(object);
4800 int length = object->IsJSArray()
4801 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
4802 : elements->length();
4803 int used = object->GetFastElementsUsage();
4804 Handle<SeededNumberDictionary> dictionary =
4805 SeededNumberDictionary::New(isolate, used);
4806 return CopyFastElementsToDictionary(elements, length, dictionary);
4807 }
4808
4809
4792 Handle<SeededNumberDictionary> JSObject::NormalizeElements( 4810 Handle<SeededNumberDictionary> JSObject::NormalizeElements(
4793 Handle<JSObject> object) { 4811 Handle<JSObject> object) {
4794 DCHECK(!object->HasExternalArrayElements() && 4812 DCHECK(!object->HasExternalArrayElements() &&
4795 !object->HasFixedTypedArrayElements()); 4813 !object->HasFixedTypedArrayElements());
4796 Isolate* isolate = object->GetIsolate(); 4814 Isolate* isolate = object->GetIsolate();
4797 4815
4798 // Find the backing store. 4816 // Find the backing store.
4799 Handle<FixedArrayBase> array(FixedArrayBase::cast(object->elements())); 4817 Handle<FixedArrayBase> elements(object->elements(), isolate);
4800 bool is_arguments = 4818 bool is_arguments = object->HasSloppyArgumentsElements();
4801 (array->map() == isolate->heap()->sloppy_arguments_elements_map());
4802 if (is_arguments) { 4819 if (is_arguments) {
4803 array = handle(FixedArrayBase::cast( 4820 FixedArray* parameter_map = FixedArray::cast(*elements);
4804 Handle<FixedArray>::cast(array)->get(1))); 4821 elements = handle(FixedArrayBase::cast(parameter_map->get(1)), isolate);
4805 } 4822 }
4806 if (array->IsDictionary()) return Handle<SeededNumberDictionary>::cast(array); 4823
4824 if (elements->IsDictionary()) {
4825 return Handle<SeededNumberDictionary>::cast(elements);
4826 }
4807 4827
4808 DCHECK(object->HasFastSmiOrObjectElements() || 4828 DCHECK(object->HasFastSmiOrObjectElements() ||
4809 object->HasFastDoubleElements() || 4829 object->HasFastDoubleElements() ||
4810 object->HasFastArgumentsElements()); 4830 object->HasFastArgumentsElements());
4811 4831
4812 // Ensure that notifications fire if the array or object prototypes are
4813 // normalizing.
4814 isolate->UpdateArrayProtectorOnNormalizeElements(object);
4815
4816 // Compute the effective length and allocate a new backing store.
4817 int length = object->IsJSArray()
4818 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
4819 : array->length();
4820 int old_capacity = 0;
4821 int used_elements = 0;
4822 object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
4823 Handle<SeededNumberDictionary> dictionary = 4832 Handle<SeededNumberDictionary> dictionary =
4824 SeededNumberDictionary::New(isolate, used_elements); 4833 GetNormalizedElementDictionary(object, elements);
4825
4826 dictionary = CopyFastElementsToDictionary(array, length, dictionary);
4827 4834
4828 // Switch to using the dictionary as the backing storage for elements. 4835 // Switch to using the dictionary as the backing storage for elements.
4829 ElementsKind target_kind = 4836 ElementsKind target_kind =
4830 is_arguments ? SLOW_SLOPPY_ARGUMENTS_ELEMENTS : DICTIONARY_ELEMENTS; 4837 is_arguments ? SLOW_SLOPPY_ARGUMENTS_ELEMENTS : DICTIONARY_ELEMENTS;
4831 Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, target_kind); 4838 Handle<Map> new_map = JSObject::GetElementsTransitionMap(object, target_kind);
4832 // Set the new map first to satify the elements type assert in set_elements(). 4839 // Set the new map first to satify the elements type assert in set_elements().
4833 JSObject::MigrateToMap(object, new_map); 4840 JSObject::MigrateToMap(object, new_map);
4834 4841
4835 if (is_arguments) { 4842 if (is_arguments) {
4836 FixedArray::cast(object->elements())->set(1, *dictionary); 4843 FixedArray::cast(object->elements())->set(1, *dictionary);
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after
5456 if (IsJSGlobalProxy()) { 5463 if (IsJSGlobalProxy()) {
5457 PrototypeIterator iter(GetIsolate(), this); 5464 PrototypeIterator iter(GetIsolate(), this);
5458 if (iter.IsAtEnd()) return false; 5465 if (iter.IsAtEnd()) return false;
5459 DCHECK(iter.GetCurrent()->IsJSGlobalObject()); 5466 DCHECK(iter.GetCurrent()->IsJSGlobalObject());
5460 return JSObject::cast(iter.GetCurrent())->map()->is_extensible(); 5467 return JSObject::cast(iter.GetCurrent())->map()->is_extensible();
5461 } 5468 }
5462 return map()->is_extensible(); 5469 return map()->is_extensible();
5463 } 5470 }
5464 5471
5465 5472
5466 Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
5467 Handle<JSObject> object) {
5468 DCHECK(!object->elements()->IsDictionary());
5469 Isolate* isolate = object->GetIsolate();
5470 int length = object->IsJSArray()
5471 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
5472 : object->elements()->length();
5473 if (length > 0) {
5474 int capacity = 0;
5475 int used = 0;
5476 object->GetElementsCapacityAndUsage(&capacity, &used);
5477 Handle<SeededNumberDictionary> new_element_dictionary =
5478 SeededNumberDictionary::New(isolate, used);
5479
5480 // Move elements to a dictionary; avoid calling NormalizeElements to avoid
5481 // unnecessary transitions.
5482 return CopyFastElementsToDictionary(handle(object->elements()), length,
5483 new_element_dictionary);
5484 }
5485 // No existing elements, use a pre-allocated empty backing store
5486 return isolate->factory()->empty_slow_element_dictionary();
5487 }
5488
5489
5490 template <typename Dictionary> 5473 template <typename Dictionary>
5491 static void ApplyAttributesToDictionary(Dictionary* dictionary, 5474 static void ApplyAttributesToDictionary(Dictionary* dictionary,
5492 const PropertyAttributes attributes) { 5475 const PropertyAttributes attributes) {
5493 int capacity = dictionary->Capacity(); 5476 int capacity = dictionary->Capacity();
5494 for (int i = 0; i < capacity; i++) { 5477 for (int i = 0; i < capacity; i++) {
5495 Object* k = dictionary->KeyAt(i); 5478 Object* k = dictionary->KeyAt(i);
5496 if (dictionary->IsKey(k) && 5479 if (dictionary->IsKey(k) &&
5497 !(k->IsSymbol() && Symbol::cast(k)->is_private())) { 5480 !(k->IsSymbol() && Symbol::cast(k)->is_private())) {
5498 PropertyDetails details = dictionary->DetailsAt(i); 5481 PropertyDetails details = dictionary->DetailsAt(i);
5499 int attrs = attributes; 5482 int attrs = attributes;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
5537 5520
5538 // It's not possible to seal or freeze objects with external array elements 5521 // It's not possible to seal or freeze objects with external array elements
5539 if (object->HasExternalArrayElements() || 5522 if (object->HasExternalArrayElements() ||
5540 object->HasFixedTypedArrayElements()) { 5523 object->HasFixedTypedArrayElements()) {
5541 THROW_NEW_ERROR( 5524 THROW_NEW_ERROR(
5542 isolate, NewTypeError(MessageTemplate::kCannotPreventExtExternalArray), 5525 isolate, NewTypeError(MessageTemplate::kCannotPreventExtExternalArray),
5543 Object); 5526 Object);
5544 } 5527 }
5545 5528
5546 Handle<SeededNumberDictionary> new_element_dictionary; 5529 Handle<SeededNumberDictionary> new_element_dictionary;
5547 if (!object->elements()->IsDictionary()) { 5530 if (!object->HasDictionaryElements()) {
5548 new_element_dictionary = GetNormalizedElementDictionary(object); 5531 int length =
5549 isolate->UpdateArrayProtectorOnNormalizeElements(object); 5532 object->IsJSArray()
5533 ? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
5534 : object->elements()->length();
5535 new_element_dictionary =
5536 length == 0 ? isolate->factory()->empty_slow_element_dictionary()
Igor Sheludko 2015/07/15 14:58:06 Probably it would be better if GetNormalizedElemen
Toon Verwaest 2015/07/15 15:07:58 It, unfortunately, cannot. The empty dictionary is
5537 : GetNormalizedElementDictionary(
5538 object, handle(object->elements()));
5550 } 5539 }
5551 5540
5552 Handle<Symbol> transition_marker; 5541 Handle<Symbol> transition_marker;
5553 if (attrs == NONE) { 5542 if (attrs == NONE) {
5554 transition_marker = isolate->factory()->nonextensible_symbol(); 5543 transition_marker = isolate->factory()->nonextensible_symbol();
5555 } else if (attrs == SEALED) { 5544 } else if (attrs == SEALED) {
5556 transition_marker = isolate->factory()->sealed_symbol(); 5545 transition_marker = isolate->factory()->sealed_symbol();
5557 } else { 5546 } else {
5558 DCHECK(attrs == FROZEN); 5547 DCHECK(attrs == FROZEN);
5559 transition_marker = isolate->factory()->frozen_symbol(); 5548 transition_marker = isolate->factory()->frozen_symbol();
(...skipping 6581 matching lines...) Expand 10 before | Expand all | Expand 10 after
12141 *new_capacity = JSObject::NewElementsCapacity(index + 1); 12130 *new_capacity = JSObject::NewElementsCapacity(index + 1);
12142 DCHECK_LT(index, *new_capacity); 12131 DCHECK_LT(index, *new_capacity);
12143 if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength || 12132 if (*new_capacity <= JSObject::kMaxUncheckedOldFastElementsLength ||
12144 (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength && 12133 (*new_capacity <= JSObject::kMaxUncheckedFastElementsLength &&
12145 object->GetHeap()->InNewSpace(object))) { 12134 object->GetHeap()->InNewSpace(object))) {
12146 return false; 12135 return false;
12147 } 12136 }
12148 // If the fast-case backing storage takes up roughly three times as 12137 // If the fast-case backing storage takes up roughly three times as
12149 // much space (in machine words) as a dictionary backing storage 12138 // much space (in machine words) as a dictionary backing storage
12150 // would, the object should have slow elements. 12139 // would, the object should have slow elements.
12151 int old_capacity = 0; 12140 int used_elements = object->GetFastElementsUsage();
12152 int used_elements = 0;
12153 object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
12154 int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) * 12141 int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
12155 SeededNumberDictionary::kEntrySize; 12142 SeededNumberDictionary::kEntrySize;
12156 return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity; 12143 return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity;
12157 } 12144 }
12158 12145
12159 12146
12160 bool JSObject::WouldConvertToSlowElements(uint32_t index) { 12147 bool JSObject::WouldConvertToSlowElements(uint32_t index) {
12161 if (HasFastElements()) { 12148 if (HasFastElements()) {
12162 Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements())); 12149 Handle<FixedArrayBase> backing_store(FixedArrayBase::cast(elements()));
12163 uint32_t capacity = static_cast<uint32_t>(backing_store->length()); 12150 uint32_t capacity = static_cast<uint32_t>(backing_store->length());
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after
12510 MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) { 12497 MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) {
12511 Isolate* isolate = array->GetIsolate(); 12498 Isolate* isolate = array->GetIsolate();
12512 Handle<Name> length = isolate->factory()->length_string(); 12499 Handle<Name> length = isolate->factory()->length_string();
12513 THROW_NEW_ERROR( 12500 THROW_NEW_ERROR(
12514 isolate, 12501 isolate,
12515 NewTypeError(MessageTemplate::kStrictReadOnlyProperty, length, array), 12502 NewTypeError(MessageTemplate::kStrictReadOnlyProperty, length, array),
12516 Object); 12503 Object);
12517 } 12504 }
12518 12505
12519 12506
12520 void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) { 12507 template <typename BackingStore>
12521 *capacity = 0; 12508 static int FastHoleyElementsUsage(JSObject* object, BackingStore* store) {
12522 *used = 0; 12509 int limit = object->IsJSArray()
12510 ? Smi::cast(JSArray::cast(object)->length())->value()
12511 : store->length();
12512 int used = 0;
12513 for (int i = 0; i < limit; ++i) {
12514 if (!store->is_the_hole(i)) ++used;
12515 }
12516 return used;
12517 }
12523 12518
12524 FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements()); 12519
12525 FixedArray* backing_store = NULL; 12520 int JSObject::GetFastElementsUsage() {
12521 FixedArrayBase* store = elements();
12526 switch (GetElementsKind()) { 12522 switch (GetElementsKind()) {
12523 case FAST_SMI_ELEMENTS:
12524 case FAST_DOUBLE_ELEMENTS:
12525 case FAST_ELEMENTS:
12526 // Only JSArray have packed elements.
12527 return Smi::cast(JSArray::cast(this)->length())->value();
12527 case FAST_SLOPPY_ARGUMENTS_ELEMENTS: 12528 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
12528 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: 12529 store = FixedArray::cast(FixedArray::cast(store)->get(1));
12529 backing_store_base = 12530 // Fall through.
12530 FixedArray::cast(FixedArray::cast(backing_store_base)->get(1));
12531 backing_store = FixedArray::cast(backing_store_base);
12532 if (backing_store->IsDictionary()) {
12533 SeededNumberDictionary* dictionary =
12534 SeededNumberDictionary::cast(backing_store);
12535 *capacity = dictionary->Capacity();
12536 *used = dictionary->NumberOfElements();
12537 break;
12538 }
12539 // Fall through.
12540 case FAST_SMI_ELEMENTS:
12541 case FAST_ELEMENTS:
12542 if (IsJSArray()) {
12543 *capacity = backing_store_base->length();
12544 *used = Smi::cast(JSArray::cast(this)->length())->value();
12545 break;
12546 }
12547 // Fall through if packing is not guaranteed.
12548 case FAST_HOLEY_SMI_ELEMENTS: 12531 case FAST_HOLEY_SMI_ELEMENTS:
12549 case FAST_HOLEY_ELEMENTS: 12532 case FAST_HOLEY_ELEMENTS:
12550 backing_store = FixedArray::cast(backing_store_base); 12533 return FastHoleyElementsUsage(this, FixedArray::cast(store));
12551 *capacity = backing_store->length(); 12534 case FAST_HOLEY_DOUBLE_ELEMENTS:
12552 for (int i = 0; i < *capacity; ++i) { 12535 if (elements()->length() == 0) return 0;
12553 if (!backing_store->get(i)->IsTheHole()) ++(*used); 12536 return FastHoleyElementsUsage(this, FixedDoubleArray::cast(store));
12554 }
12555 break;
12556 case DICTIONARY_ELEMENTS: {
12557 SeededNumberDictionary* dictionary = element_dictionary();
12558 *capacity = dictionary->Capacity();
12559 *used = dictionary->NumberOfElements();
12560 break;
12561 }
12562 case FAST_DOUBLE_ELEMENTS:
12563 if (IsJSArray()) {
12564 *capacity = backing_store_base->length();
12565 *used = Smi::cast(JSArray::cast(this)->length())->value();
12566 break;
12567 }
12568 // Fall through if packing is not guaranteed.
12569 case FAST_HOLEY_DOUBLE_ELEMENTS: {
12570 *capacity = elements()->length();
12571 if (*capacity == 0) break;
12572 FixedDoubleArray * elms = FixedDoubleArray::cast(elements());
12573 for (int i = 0; i < *capacity; i++) {
12574 if (!elms->is_the_hole(i)) ++(*used);
12575 }
12576 break;
12577 }
12578 12537
12538 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
12539 case DICTIONARY_ELEMENTS:
12579 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ 12540 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
12580 case EXTERNAL_##TYPE##_ELEMENTS: \ 12541 case EXTERNAL_##TYPE##_ELEMENTS: \
12581 case TYPE##_ELEMENTS: \ 12542 case TYPE##_ELEMENTS: \
12582 12543
12583 TYPED_ARRAYS(TYPED_ARRAY_CASE) 12544 TYPED_ARRAYS(TYPED_ARRAY_CASE)
12584 #undef TYPED_ARRAY_CASE 12545 #undef TYPED_ARRAY_CASE
12585 { 12546 UNREACHABLE();
12586 // External arrays are considered 100% used. 12547 return 0;
12587 FixedArrayBase* external_array = FixedArrayBase::cast(elements());
12588 *capacity = external_array->length();
12589 *used = external_array->length();
12590 break;
12591 }
12592 } 12548 }
12593 } 12549 }
12594 12550
12595 12551
12596 // Certain compilers request function template instantiation when they 12552 // Certain compilers request function template instantiation when they
12597 // see the definition of the other template functions in the 12553 // see the definition of the other template functions in the
12598 // class. This requires us to have the template functions put 12554 // class. This requires us to have the template functions put
12599 // together, so even though this function belongs in objects-debug.cc, 12555 // together, so even though this function belongs in objects-debug.cc,
12600 // we keep it here instead to satisfy certain compilers. 12556 // we keep it here instead to satisfy certain compilers.
12601 #ifdef OBJECT_PRINT 12557 #ifdef OBJECT_PRINT
(...skipping 3321 matching lines...) Expand 10 before | Expand all | Expand 10 after
15923 Handle<Object> new_value) { 15879 Handle<Object> new_value) {
15924 if (cell->value() != *new_value) { 15880 if (cell->value() != *new_value) {
15925 cell->set_value(*new_value); 15881 cell->set_value(*new_value);
15926 Isolate* isolate = cell->GetIsolate(); 15882 Isolate* isolate = cell->GetIsolate();
15927 cell->dependent_code()->DeoptimizeDependentCodeGroup( 15883 cell->dependent_code()->DeoptimizeDependentCodeGroup(
15928 isolate, DependentCode::kPropertyCellChangedGroup); 15884 isolate, DependentCode::kPropertyCellChangedGroup);
15929 } 15885 }
15930 } 15886 }
15931 } // namespace internal 15887 } // namespace internal
15932 } // namespace v8 15888 } // namespace v8
OLDNEW
« no previous file with comments | « src/objects.h ('k') | test/cctest/test-api.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698