Fix setting array length to be ES5 conform.

This also refactors the way we set the length of an arrays' backing
store to use the new elements accessor interface. The actual fix is in
DictionaryElementsAccessor::SetLengthWithoutNormalize() where we first
search for non-deletable elements according to ES5 section 15.4.5.2
specifications.

Snippet from the specification: Attempting to set the length property of
an Array object to a value that is numerically less than or equal to the
largest numeric property name of an existing array indexed non-deletable
property of the array will result in the length being set to a numeric
value that is one greater than that largest numeric property name.

R=danno@chromium.org
TEST=test262/15.4.4.??-7-b-16

Review URL: http://codereview.chromium.org/8372064

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9911 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9b968b7d
This diff is collapsed.
......@@ -44,6 +44,11 @@ class ElementsAccessor {
JSObject* holder,
Object* receiver) = 0;
// Modifies the length data property as specified for JSArrays and resizes
// the underlying backing store accordingly.
virtual MaybeObject* SetLength(JSObject* holder,
Object* new_length) = 0;
virtual MaybeObject* Delete(JSObject* holder,
uint32_t key,
JSReceiver::DeleteMode mode) = 0;
......
......@@ -8351,61 +8351,6 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
}
MaybeObject* JSObject::SetSlowElements(Object* len) {
// We should never end in here with a pixel or external array.
ASSERT(!HasExternalArrayElements());
uint32_t new_length = static_cast<uint32_t>(len->Number());
FixedArrayBase* old_elements = elements();
ElementsKind elements_kind = GetElementsKind();
switch (elements_kind) {
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS: {
// Make sure we never try to shrink dense arrays into sparse arrays.
ASSERT(static_cast<uint32_t>(old_elements->length()) <= new_length);
MaybeObject* result = NormalizeElements();
if (result->IsFailure()) return result;
// Update length for JSArrays.
if (IsJSArray()) JSArray::cast(this)->set_length(len);
break;
}
case DICTIONARY_ELEMENTS: {
if (IsJSArray()) {
uint32_t old_length =
static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
element_dictionary()->RemoveNumberEntries(new_length, old_length),
JSArray::cast(this)->set_length(len);
}
break;
}
case NON_STRICT_ARGUMENTS_ELEMENTS:
UNIMPLEMENTED();
break;
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
case EXTERNAL_SHORT_ELEMENTS:
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case EXTERNAL_PIXEL_ELEMENTS:
UNREACHABLE();
break;
}
if (FLAG_trace_elements_transitions) {
PrintElementsTransition(stdout, elements_kind, old_elements,
DICTIONARY_ELEMENTS, elements());
}
return this;
}
MaybeObject* JSArray::Initialize(int capacity) {
Heap* heap = GetHeap();
ASSERT(capacity >= 0);
......@@ -8437,165 +8382,10 @@ void JSArray::Expand(int required_size) {
}
static Failure* ArrayLengthRangeError(Heap* heap) {
HandleScope scope(heap->isolate());
return heap->isolate()->Throw(
*FACTORY->NewRangeError("invalid_array_length",
HandleVector<Object>(NULL, 0)));
}
MaybeObject* JSObject::SetElementsLength(Object* len) {
// We should never end in here with a pixel or external array.
ASSERT(AllowsSetElementsLength());
MaybeObject* maybe_smi_length = len->ToSmi();
Object* smi_length = Smi::FromInt(0);
if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) {
const int value = Smi::cast(smi_length)->value();
if (value < 0) return ArrayLengthRangeError(GetHeap());
ElementsKind elements_kind = GetElementsKind();
switch (elements_kind) {
case FAST_SMI_ONLY_ELEMENTS:
case FAST_ELEMENTS:
case FAST_DOUBLE_ELEMENTS: {
int old_capacity = FixedArrayBase::cast(elements())->length();
if (value <= old_capacity) {
if (IsJSArray()) {
Object* obj;
if (elements_kind == FAST_ELEMENTS ||
elements_kind == FAST_SMI_ONLY_ELEMENTS) {
MaybeObject* maybe_obj = EnsureWritableFastElements();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
if (2 * value <= old_capacity) {
// If more than half the elements won't be used, trim the array.
if (value == 0) {
initialize_elements();
} else {
Address filler_start;
int filler_size;
if (elements_kind == FAST_ELEMENTS ||
elements_kind == FAST_SMI_ONLY_ELEMENTS) {
FixedArray* fast_elements = FixedArray::cast(elements());
fast_elements->set_length(value);
filler_start = fast_elements->address() +
FixedArray::OffsetOfElementAt(value);
filler_size = (old_capacity - value) * kPointerSize;
} else {
ASSERT(GetElementsKind() == FAST_DOUBLE_ELEMENTS);
FixedDoubleArray* fast_double_elements =
FixedDoubleArray::cast(elements());
fast_double_elements->set_length(value);
filler_start = fast_double_elements->address() +
FixedDoubleArray::OffsetOfElementAt(value);
filler_size = (old_capacity - value) * kDoubleSize;
}
GetHeap()->CreateFillerObjectAt(filler_start, filler_size);
}
} else {
// Otherwise, fill the unused tail with holes.
int old_length = FastD2I(JSArray::cast(this)->length()->Number());
if (elements_kind == FAST_ELEMENTS ||
elements_kind == FAST_SMI_ONLY_ELEMENTS) {
FixedArray* fast_elements = FixedArray::cast(elements());
for (int i = value; i < old_length; i++) {
fast_elements->set_the_hole(i);
}
} else {
ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS);
FixedDoubleArray* fast_double_elements =
FixedDoubleArray::cast(elements());
for (int i = value; i < old_length; i++) {
fast_double_elements->set_the_hole(i);
}
}
}
JSArray::cast(this)->set_length(Smi::cast(smi_length));
}
return this;
}
int min = NewElementsCapacity(old_capacity);
int new_capacity = value > min ? value : min;
if (!ShouldConvertToSlowElements(new_capacity)) {
MaybeObject* result;
if (elements_kind == FAST_ELEMENTS ||
elements_kind == FAST_SMI_ONLY_ELEMENTS) {
SetFastElementsCapacityMode set_capacity_mode =
elements_kind == FAST_SMI_ONLY_ELEMENTS
? kAllowSmiOnlyElements
: kDontAllowSmiOnlyElements;
result = SetFastElementsCapacityAndLength(new_capacity,
value,
set_capacity_mode);
} else {
ASSERT(elements_kind == FAST_DOUBLE_ELEMENTS);
result = SetFastDoubleElementsCapacityAndLength(new_capacity,
value);
}
if (result->IsFailure()) return result;
return this;
}
break;
}
case DICTIONARY_ELEMENTS: {
if (IsJSArray()) {
if (value == 0) {
// If the length of a slow array is reset to zero, we clear
// the array and flush backing storage. This has the added
// benefit that the array returns to fast mode.
Object* obj;
{ MaybeObject* maybe_obj = ResetElements();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
} else {
// Remove deleted elements.
uint32_t old_length =
static_cast<uint32_t>(JSArray::cast(this)->length()->Number());
element_dictionary()->RemoveNumberEntries(value, old_length);
}
JSArray::cast(this)->set_length(Smi::cast(smi_length));
}
return this;
}
case NON_STRICT_ARGUMENTS_ELEMENTS:
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
case EXTERNAL_SHORT_ELEMENTS:
case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
case EXTERNAL_INT_ELEMENTS:
case EXTERNAL_UNSIGNED_INT_ELEMENTS:
case EXTERNAL_FLOAT_ELEMENTS:
case EXTERNAL_DOUBLE_ELEMENTS:
case EXTERNAL_PIXEL_ELEMENTS:
UNREACHABLE();
break;
}
}
// General slow case.
if (len->IsNumber()) {
uint32_t length;
if (len->ToArrayIndex(&length)) {
return SetSlowElements(len);
} else {
return ArrayLengthRangeError(GetHeap());
}
}
// len is not a number so make the array size one and
// set only element to len.
Object* obj;
MaybeObject* maybe_obj = GetHeap()->AllocateFixedArray(1);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
FixedArray::cast(obj)->set(0, len);
maybe_obj = EnsureCanContainElements(&len, 1);
if (maybe_obj->IsFailure()) return maybe_obj;
if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(1));
set_elements(FixedArray::cast(obj));
return this;
return GetElementsAccessor()->SetLength(this, len);
}
......@@ -11971,30 +11761,6 @@ MaybeObject* Dictionary<Shape, Key>::EnsureCapacity(int n, Key key) {
}
void NumberDictionary::RemoveNumberEntries(uint32_t from, uint32_t to) {
// Do nothing if the interval [from, to) is empty.
if (from >= to) return;
Heap* heap = GetHeap();
int removed_entries = 0;
Object* the_hole_value = heap->the_hole_value();
int capacity = Capacity();
for (int i = 0; i < capacity; i++) {
Object* key = KeyAt(i);
if (key->IsNumber()) {
uint32_t number = static_cast<uint32_t>(key->Number());
if (from <= number && number < to) {
SetEntry(i, the_hole_value, the_hole_value);
removed_entries++;
}
}
}
// Update the number of elements.
ElementsRemoved(removed_entries);
}
template<typename Shape, typename Key>
Object* Dictionary<Shape, Key>::DeleteProperty(int entry,
JSReceiver::DeleteMode mode) {
......
......@@ -1659,7 +1659,6 @@ class JSObject: public JSReceiver {
MUST_USE_RESULT MaybeObject* SetFastDoubleElementsCapacityAndLength(
int capacity,
int length);
MUST_USE_RESULT MaybeObject* SetSlowElements(Object* length);
// Lookup interceptors are used for handling properties controlled by host
// objects.
......@@ -2911,9 +2910,6 @@ class NumberDictionary: public Dictionary<NumberDictionaryShape, uint32_t> {
// requires_slow_elements returns false.
inline uint32_t max_number_key();
// Remove all entries were key is a number and (from <= key && key < to).
void RemoveNumberEntries(uint32_t from, uint32_t to);
// Bit masks.
static const int kRequiresSlowElementsMask = 1;
static const int kRequiresSlowElementsTagSize = 1;
......
......@@ -499,39 +499,6 @@ S15.4.4.3_A2_T1: FAIL_OK
15.2.3.6-4-623: FAIL
# Bug? ES5 Attributes - all attributes in Date.prototype.toJSON are correct
15.2.3.6-4-624: FAIL
# Bug? Array.prototype.indexOf - decreasing length of array does not delete
# non-configurable properties
15.4.4.14-9-a-19: FAIL
# Bug? Array.prototype.lastIndexOf - decreasing length of array does not delete
# non-configurable properties
15.4.4.15-8-a-19: FAIL
# Bug? Array.prototype.every - decreasing length of array does not delete
# non-configurable properties
15.4.4.16-7-b-16: FAIL
# Bug? Array.prototype.some - decreasing length of array does not delete
# non-configurable properties
15.4.4.17-7-b-16: FAIL
# Bug? Array.prototype.forEach - decreasing length of array does not delete
# non-configurable properties
15.4.4.18-7-b-16: FAIL
# Bug? Array.prototype.map - decreasing length of array does not delete
# non-configurable properties
15.4.4.19-8-b-16: FAIL
# Bug? Array.prototype.filter - decreasing length of array does not delete
# non-configurable properties
15.4.4.20-9-b-16: FAIL
# Bug? Array.prototype.reduce - decreasing length of array in step 8 does not
# delete non-configurable properties
15.4.4.21-9-b-16: FAIL
# Bug? Array.prototype.reduce - decreasing length of array does not delete
# non-configurable properties
15.4.4.21-9-b-29: FAIL
# Bug? Array.prototype.reduceRight - decreasing length of array in step 8 does
# not delete non-configurable properties
15.4.4.22-9-b-16: FAIL
# Bug? Array.prototype.reduceRight - decreasing length of array does not delete
# non-configurable properties
15.4.4.22-9-b-29: FAIL
############################ SKIPPED TESTS #############################
......
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