Cleanup element normalization logic

BUG=

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

Cr-Commit-Position: refs/heads/master@{#29686}
This commit is contained in:
verwaest 2015-07-15 08:57:27 -07:00 committed by Commit bot
parent 597af29260
commit 99b59d16bf
4 changed files with 75 additions and 116 deletions

View File

@ -2437,7 +2437,9 @@ bool Isolate::IsFastArrayConstructorPrototypeChainIntact() {
return cell_reports_intact;
}
if (initial_array_proto->elements() != heap()->empty_fixed_array()) {
FixedArrayBase* elements = initial_array_proto->elements();
if (elements != heap()->empty_fixed_array() &&
elements != heap()->empty_slow_element_dictionary()) {
DCHECK_EQ(false, cell_reports_intact);
return cell_reports_intact;
}
@ -2448,7 +2450,10 @@ bool Isolate::IsFastArrayConstructorPrototypeChainIntact() {
DCHECK_EQ(false, cell_reports_intact);
return cell_reports_intact;
}
if (initial_object_proto->elements() != heap()->empty_fixed_array()) {
elements = initial_object_proto->elements();
if (elements != heap()->empty_fixed_array() &&
elements != heap()->empty_slow_element_dictionary()) {
DCHECK_EQ(false, cell_reports_intact);
return cell_reports_intact;
}

View File

@ -4799,6 +4799,24 @@ void JSObject::RequireSlowElements(SeededNumberDictionary* dictionary) {
}
Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
Handle<JSObject> object, Handle<FixedArrayBase> elements) {
DCHECK(!object->HasDictionaryElements());
DCHECK(!object->HasSlowArgumentsElements());
Isolate* isolate = object->GetIsolate();
// Ensure that notifications fire if the array or object prototypes are
// normalizing.
isolate->UpdateArrayProtectorOnNormalizeElements(object);
int length = object->IsJSArray()
? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
: elements->length();
int used = object->GetFastElementsUsage();
Handle<SeededNumberDictionary> dictionary =
SeededNumberDictionary::New(isolate, used);
return CopyFastElementsToDictionary(elements, length, dictionary);
}
Handle<SeededNumberDictionary> JSObject::NormalizeElements(
Handle<JSObject> object) {
DCHECK(!object->HasExternalArrayElements() &&
@ -4806,34 +4824,23 @@ Handle<SeededNumberDictionary> JSObject::NormalizeElements(
Isolate* isolate = object->GetIsolate();
// Find the backing store.
Handle<FixedArrayBase> array(FixedArrayBase::cast(object->elements()));
bool is_arguments =
(array->map() == isolate->heap()->sloppy_arguments_elements_map());
Handle<FixedArrayBase> elements(object->elements(), isolate);
bool is_arguments = object->HasSloppyArgumentsElements();
if (is_arguments) {
array = handle(FixedArrayBase::cast(
Handle<FixedArray>::cast(array)->get(1)));
FixedArray* parameter_map = FixedArray::cast(*elements);
elements = handle(FixedArrayBase::cast(parameter_map->get(1)), isolate);
}
if (elements->IsDictionary()) {
return Handle<SeededNumberDictionary>::cast(elements);
}
if (array->IsDictionary()) return Handle<SeededNumberDictionary>::cast(array);
DCHECK(object->HasFastSmiOrObjectElements() ||
object->HasFastDoubleElements() ||
object->HasFastArgumentsElements());
// Ensure that notifications fire if the array or object prototypes are
// normalizing.
isolate->UpdateArrayProtectorOnNormalizeElements(object);
// Compute the effective length and allocate a new backing store.
int length = object->IsJSArray()
? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
: array->length();
int old_capacity = 0;
int used_elements = 0;
object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
Handle<SeededNumberDictionary> dictionary =
SeededNumberDictionary::New(isolate, used_elements);
dictionary = CopyFastElementsToDictionary(array, length, dictionary);
GetNormalizedElementDictionary(object, elements);
// Switch to using the dictionary as the backing storage for elements.
ElementsKind target_kind =
@ -5473,30 +5480,6 @@ bool JSObject::IsExtensible() {
}
Handle<SeededNumberDictionary> JSObject::GetNormalizedElementDictionary(
Handle<JSObject> object) {
DCHECK(!object->elements()->IsDictionary());
Isolate* isolate = object->GetIsolate();
int length = object->IsJSArray()
? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
: object->elements()->length();
if (length > 0) {
int capacity = 0;
int used = 0;
object->GetElementsCapacityAndUsage(&capacity, &used);
Handle<SeededNumberDictionary> new_element_dictionary =
SeededNumberDictionary::New(isolate, used);
// Move elements to a dictionary; avoid calling NormalizeElements to avoid
// unnecessary transitions.
return CopyFastElementsToDictionary(handle(object->elements()), length,
new_element_dictionary);
}
// No existing elements, use a pre-allocated empty backing store
return isolate->factory()->empty_slow_element_dictionary();
}
template <typename Dictionary>
static void ApplyAttributesToDictionary(Dictionary* dictionary,
const PropertyAttributes attributes) {
@ -5554,9 +5537,15 @@ MaybeHandle<Object> JSObject::PreventExtensionsWithTransition(
}
Handle<SeededNumberDictionary> new_element_dictionary;
if (!object->elements()->IsDictionary()) {
new_element_dictionary = GetNormalizedElementDictionary(object);
isolate->UpdateArrayProtectorOnNormalizeElements(object);
if (!object->HasDictionaryElements()) {
int length =
object->IsJSArray()
? Smi::cast(Handle<JSArray>::cast(object)->length())->value()
: object->elements()->length();
new_element_dictionary =
length == 0 ? isolate->factory()->empty_slow_element_dictionary()
: GetNormalizedElementDictionary(
object, handle(object->elements()));
}
Handle<Symbol> transition_marker;
@ -12130,9 +12119,7 @@ static bool ShouldConvertToSlowElements(JSObject* object, uint32_t 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;
object->GetElementsCapacityAndUsage(&old_capacity, &used_elements);
int used_elements = object->GetFastElementsUsage();
int dictionary_size = SeededNumberDictionary::ComputeCapacity(used_elements) *
SeededNumberDictionary::kEntrySize;
return 3 * static_cast<uint32_t>(dictionary_size) <= *new_capacity;
@ -12499,79 +12486,48 @@ MaybeHandle<Object> JSArray::ReadOnlyLengthError(Handle<JSArray> array) {
}
void JSObject::GetElementsCapacityAndUsage(int* capacity, int* used) {
*capacity = 0;
*used = 0;
template <typename BackingStore>
static int FastHoleyElementsUsage(JSObject* object, BackingStore* store) {
int limit = object->IsJSArray()
? Smi::cast(JSArray::cast(object)->length())->value()
: store->length();
int used = 0;
for (int i = 0; i < limit; ++i) {
if (!store->is_the_hole(i)) ++used;
}
return used;
}
FixedArrayBase* backing_store_base = FixedArrayBase::cast(elements());
FixedArray* backing_store = NULL;
int JSObject::GetFastElementsUsage() {
FixedArrayBase* store = elements();
switch (GetElementsKind()) {
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
backing_store_base =
FixedArray::cast(FixedArray::cast(backing_store_base)->get(1));
backing_store = FixedArray::cast(backing_store_base);
if (backing_store->IsDictionary()) {
SeededNumberDictionary* dictionary =
SeededNumberDictionary::cast(backing_store);
*capacity = dictionary->Capacity();
*used = dictionary->NumberOfElements();
break;
}
// Fall through.
case FAST_SMI_ELEMENTS:
case FAST_DOUBLE_ELEMENTS:
case FAST_ELEMENTS:
if (IsJSArray()) {
*capacity = backing_store_base->length();
*used = Smi::cast(JSArray::cast(this)->length())->value();
break;
}
// Fall through if packing is not guaranteed.
// Only JSArray have packed elements.
return Smi::cast(JSArray::cast(this)->length())->value();
case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
store = FixedArray::cast(FixedArray::cast(store)->get(1));
// Fall through.
case FAST_HOLEY_SMI_ELEMENTS:
case FAST_HOLEY_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()) ++(*used);
}
break;
case DICTIONARY_ELEMENTS: {
SeededNumberDictionary* dictionary = element_dictionary();
*capacity = dictionary->Capacity();
*used = dictionary->NumberOfElements();
break;
}
case FAST_DOUBLE_ELEMENTS:
if (IsJSArray()) {
*capacity = backing_store_base->length();
*used = Smi::cast(JSArray::cast(this)->length())->value();
break;
}
// Fall through if packing is not guaranteed.
case FAST_HOLEY_DOUBLE_ELEMENTS: {
*capacity = elements()->length();
if (*capacity == 0) break;
FixedDoubleArray * elms = FixedDoubleArray::cast(elements());
for (int i = 0; i < *capacity; i++) {
if (!elms->is_the_hole(i)) ++(*used);
}
break;
}
return FastHoleyElementsUsage(this, FixedArray::cast(store));
case FAST_HOLEY_DOUBLE_ELEMENTS:
if (elements()->length() == 0) return 0;
return FastHoleyElementsUsage(this, FixedDoubleArray::cast(store));
case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
case DICTIONARY_ELEMENTS:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case EXTERNAL_##TYPE##_ELEMENTS: \
case TYPE##_ELEMENTS: \
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
{
// External arrays are considered 100% used.
FixedArrayBase* external_array = FixedArrayBase::cast(elements());
*capacity = external_array->length();
*used = external_array->length();
break;
}
UNREACHABLE();
}
return 0;
}

View File

@ -2276,8 +2276,8 @@ class JSObject: public JSReceiver {
Handle<JSObject> object, const char* type, Handle<Name> name,
Handle<Object> old_value);
// Gets the current elements capacity and the number of used elements.
void GetElementsCapacityAndUsage(int* capacity, int* used);
// Gets the number of currently used elements.
int GetFastElementsUsage();
// Deletes an existing named property in a normalized object.
static void DeleteNormalizedProperty(Handle<JSObject> object,
@ -2332,7 +2332,7 @@ class JSObject: public JSReceiver {
static Handle<Smi> GetOrCreateIdentityHash(Handle<JSObject> object);
static Handle<SeededNumberDictionary> GetNormalizedElementDictionary(
Handle<JSObject> object);
Handle<JSObject> object, Handle<FixedArrayBase> elements);
// Helper for fast versions of preventExtensions, seal, and freeze.
// attrs is one of NONE, SEALED, or FROZEN (depending on the operation).

View File

@ -16976,8 +16976,6 @@ TEST(VerifyArrayPrototypeGuarantees) {
// Break fast array hole handling by prototype structure changes.
BreakArrayGuarantees("[].__proto__.__proto__ = { funny: true };");
// By sending elements to dictionary mode.
BreakArrayGuarantees("Object.freeze(Array.prototype);");
BreakArrayGuarantees("Object.freeze(Object.prototype);");
BreakArrayGuarantees(
"Object.defineProperty(Array.prototype, 0, {"
" get: function() { return 3; }});");