2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
4 * Copyright (C) 2003 Peter Kelly (pmk@post.com)
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "ArrayPrototype.h"
27 #include "ButterflyInlines.h"
28 #include "CachedCall.h"
29 #include "CopiedSpace.h"
31 #include "Executable.h"
32 #include "GetterSetter.h"
33 #include "IndexingHeaderInlines.h"
34 #include "JSCInlines.h"
35 #include "PropertyNameArray.h"
37 #include <wtf/AVLTree.h>
38 #include <wtf/Assertions.h>
39 #include <wtf/OwnPtr.h>
46 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray
);
48 const ClassInfo
JSArray::s_info
= {"Array", &JSNonFinalObject::s_info
, 0, 0, CREATE_METHOD_TABLE(JSArray
)};
50 Butterfly
* createArrayButterflyInDictionaryIndexingMode(
51 VM
& vm
, JSCell
* intendedOwner
, unsigned initialLength
)
53 Butterfly
* butterfly
= Butterfly::create(
54 vm
, intendedOwner
, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
55 ArrayStorage
* storage
= butterfly
->arrayStorage();
56 storage
->setLength(initialLength
);
57 storage
->setVectorLength(0);
58 storage
->m_indexBias
= 0;
59 storage
->m_sparseMap
.clear();
60 storage
->m_numValuesInVector
= 0;
64 void JSArray::setLengthWritable(ExecState
* exec
, bool writable
)
66 ASSERT(isLengthWritable() || !writable
);
67 if (!isLengthWritable() || writable
)
70 enterDictionaryIndexingMode(exec
->vm());
72 SparseArrayValueMap
* map
= arrayStorage()->m_sparseMap
.get();
74 map
->setLengthIsReadOnly();
77 // Defined in ES5.1 15.4.5.1
78 bool JSArray::defineOwnProperty(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, const PropertyDescriptor
& descriptor
, bool throwException
)
80 JSArray
* array
= jsCast
<JSArray
*>(object
);
82 // 3. If P is "length", then
83 if (propertyName
== exec
->propertyNames().length
) {
84 // All paths through length definition call the default [[DefineOwnProperty]], hence:
85 // from ES5.1 8.12.9 7.a.
86 if (descriptor
.configurablePresent() && descriptor
.configurable())
87 return reject(exec
, throwException
, "Attempting to change configurable attribute of unconfigurable property.");
88 // from ES5.1 8.12.9 7.b.
89 if (descriptor
.enumerablePresent() && descriptor
.enumerable())
90 return reject(exec
, throwException
, "Attempting to change enumerable attribute of unconfigurable property.");
92 // a. If the [[Value]] field of Desc is absent, then
93 // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
94 if (descriptor
.isAccessorDescriptor())
95 return reject(exec
, throwException
, "Attempting to change access mechanism for an unconfigurable property.");
96 // from ES5.1 8.12.9 10.a.
97 if (!array
->isLengthWritable() && descriptor
.writablePresent() && descriptor
.writable())
98 return reject(exec
, throwException
, "Attempting to change writable attribute of unconfigurable property.");
99 // This descriptor is either just making length read-only, or changing nothing!
100 if (!descriptor
.value()) {
101 if (descriptor
.writablePresent())
102 array
->setLengthWritable(exec
, descriptor
.writable());
106 // b. Let newLenDesc be a copy of Desc.
107 // c. Let newLen be ToUint32(Desc.[[Value]]).
108 unsigned newLen
= descriptor
.value().toUInt32(exec
);
109 // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
110 if (newLen
!= descriptor
.value().toNumber(exec
)) {
111 exec
->vm().throwException(exec
, createRangeError(exec
, "Invalid array length"));
115 // Based on SameValue check in 8.12.9, this is always okay.
116 if (newLen
== array
->length()) {
117 if (descriptor
.writablePresent())
118 array
->setLengthWritable(exec
, descriptor
.writable());
122 // e. Set newLenDesc.[[Value] to newLen.
123 // f. If newLen >= oldLen, then
124 // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
125 // g. Reject if oldLenDesc.[[Writable]] is false.
126 if (!array
->isLengthWritable())
127 return reject(exec
, throwException
, "Attempting to change value of a readonly property.");
129 // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
131 // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
132 // i.ii. Let newWritable be false.
133 // i.iii. Set newLenDesc.[[Writable] to true.
134 // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
135 // k. If succeeded is false, return false.
136 // l. While newLen < oldLen repeat,
137 // l.i. Set oldLen to oldLen – 1.
138 // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
139 // l.iii. If deleteSucceeded is false, then
140 if (!array
->setLength(exec
, newLen
, throwException
)) {
141 // 1. Set newLenDesc.[[Value] to oldLen+1.
142 // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
143 // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
145 if (descriptor
.writablePresent())
146 array
->setLengthWritable(exec
, descriptor
.writable());
150 // m. If newWritable is false, then
151 // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
152 // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
154 if (descriptor
.writablePresent())
155 array
->setLengthWritable(exec
, descriptor
.writable());
160 // 4. Else if P is an array index (15.4), then
161 // a. Let index be ToUint32(P).
162 unsigned index
= propertyName
.asIndex();
163 if (index
!= PropertyName::NotAnIndex
) {
164 // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
165 if (index
>= array
->length() && !array
->isLengthWritable())
166 return reject(exec
, throwException
, "Attempting to define numeric property on array with non-writable length property.");
167 // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
168 // d. Reject if succeeded is false.
169 // e. If index >= oldLen
170 // e.i. Set oldLenDesc.[[Value]] to index + 1.
171 // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
173 return array
->defineOwnIndexedProperty(exec
, index
, descriptor
, throwException
);
176 return array
->JSObject::defineOwnNonIndexProperty(exec
, propertyName
, descriptor
, throwException
);
179 bool JSArray::getOwnPropertySlot(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
181 JSArray
* thisObject
= jsCast
<JSArray
*>(object
);
182 if (propertyName
== exec
->propertyNames().length
) {
183 unsigned attributes
= thisObject
->isLengthWritable() ? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
;
184 slot
.setValue(thisObject
, attributes
, jsNumber(thisObject
->length()));
188 return JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
192 void JSArray::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
194 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
196 if (propertyName
== exec
->propertyNames().length
) {
197 unsigned newLength
= value
.toUInt32(exec
);
198 if (value
.toNumber(exec
) != static_cast<double>(newLength
)) {
199 exec
->vm().throwException(exec
, createRangeError(exec
, ASCIILiteral("Invalid array length")));
202 thisObject
->setLength(exec
, newLength
, slot
.isStrictMode());
206 JSObject::put(thisObject
, exec
, propertyName
, value
, slot
);
209 bool JSArray::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
211 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
213 if (propertyName
== exec
->propertyNames().length
)
216 return JSObject::deleteProperty(thisObject
, exec
, propertyName
);
219 static int compareKeysForQSort(const void* a
, const void* b
)
221 unsigned da
= *static_cast<const unsigned*>(a
);
222 unsigned db
= *static_cast<const unsigned*>(b
);
223 return (da
> db
) - (da
< db
);
226 void JSArray::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
228 JSArray
* thisObject
= jsCast
<JSArray
*>(object
);
230 if (mode
== IncludeDontEnumProperties
)
231 propertyNames
.add(exec
->propertyNames().length
);
233 JSObject::getOwnNonIndexPropertyNames(thisObject
, exec
, propertyNames
, mode
);
236 // This method makes room in the vector, but leaves the new space for count slots uncleared.
237 bool JSArray::unshiftCountSlowCase(VM
& vm
, bool addToFront
, unsigned count
)
239 ArrayStorage
* storage
= ensureArrayStorage(vm
);
240 Butterfly
* butterfly
= storage
->butterfly();
241 unsigned propertyCapacity
= structure(vm
)->outOfLineCapacity();
242 unsigned propertySize
= structure(vm
)->outOfLineSize();
244 // If not, we should have handled this on the fast path.
245 ASSERT(!addToFront
|| count
> storage
->m_indexBias
);
248 // Gather 4 key metrics:
249 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
250 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
251 // * currentCapacity - what is the current size of the vector, including any pre-capacity.
252 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
254 unsigned length
= storage
->length();
255 unsigned usedVectorLength
= min(storage
->vectorLength(), length
);
256 ASSERT(usedVectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
257 // Check that required vector length is possible, in an overflow-safe fashion.
258 if (count
> MAX_STORAGE_VECTOR_LENGTH
- usedVectorLength
)
260 unsigned requiredVectorLength
= usedVectorLength
+ count
;
261 ASSERT(requiredVectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
262 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
263 ASSERT(storage
->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH
&& (MAX_STORAGE_VECTOR_LENGTH
- storage
->vectorLength()) >= storage
->m_indexBias
);
264 unsigned currentCapacity
= storage
->vectorLength() + storage
->m_indexBias
;
265 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
266 unsigned desiredCapacity
= min(MAX_STORAGE_VECTOR_LENGTH
, max(BASE_VECTOR_LEN
, requiredVectorLength
) << 1);
269 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
271 DeferGC
deferGC(vm
.heap
);
272 void* newAllocBase
= 0;
273 unsigned newStorageCapacity
;
274 // If the current storage array is sufficiently large (but not too large!) then just keep using it.
275 if (currentCapacity
> desiredCapacity
&& isDenseEnoughForVector(currentCapacity
, requiredVectorLength
)) {
276 newAllocBase
= butterfly
->base(structure(vm
));
277 newStorageCapacity
= currentCapacity
;
279 size_t newSize
= Butterfly::totalSize(0, propertyCapacity
, true, ArrayStorage::sizeFor(desiredCapacity
));
280 if (!vm
.heap
.tryAllocateStorage(this, newSize
, &newAllocBase
))
282 newStorageCapacity
= desiredCapacity
;
286 // Work out where we're going to move things to.
288 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
289 // If we're adding to the end, we'll add all the new space to the end.
290 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
291 // If it did, we calculate the amount that will remain based on an atomic decay - leave the
292 // vector with half the post-capacity it had previously.
293 unsigned postCapacity
= 0;
295 postCapacity
= max(newStorageCapacity
- requiredVectorLength
, count
);
296 else if (length
< storage
->vectorLength()) {
297 // Atomic decay, + the post-capacity cannot be greater than what is available.
298 postCapacity
= min((storage
->vectorLength() - length
) >> 1, newStorageCapacity
- requiredVectorLength
);
299 // If we're moving contents within the same allocation, the post-capacity is being reduced.
300 ASSERT(newAllocBase
!= butterfly
->base(structure(vm
)) || postCapacity
< storage
->vectorLength() - length
);
303 unsigned newVectorLength
= requiredVectorLength
+ postCapacity
;
304 unsigned newIndexBias
= newStorageCapacity
- newVectorLength
;
306 Butterfly
* newButterfly
= Butterfly::fromBase(newAllocBase
, newIndexBias
, propertyCapacity
);
309 ASSERT(count
+ usedVectorLength
<= newVectorLength
);
310 memmove(newButterfly
->arrayStorage()->m_vector
+ count
, storage
->m_vector
, sizeof(JSValue
) * usedVectorLength
);
311 memmove(newButterfly
->propertyStorage() - propertySize
, butterfly
->propertyStorage() - propertySize
, sizeof(JSValue
) * propertySize
+ sizeof(IndexingHeader
) + ArrayStorage::sizeFor(0));
312 } else if ((newAllocBase
!= butterfly
->base(structure(vm
))) || (newIndexBias
!= storage
->m_indexBias
)) {
313 memmove(newButterfly
->propertyStorage() - propertySize
, butterfly
->propertyStorage() - propertySize
, sizeof(JSValue
) * propertySize
+ sizeof(IndexingHeader
) + ArrayStorage::sizeFor(0));
314 memmove(newButterfly
->arrayStorage()->m_vector
, storage
->m_vector
, sizeof(JSValue
) * usedVectorLength
);
316 WriteBarrier
<Unknown
>* newVector
= newButterfly
->arrayStorage()->m_vector
;
317 for (unsigned i
= requiredVectorLength
; i
< newVectorLength
; i
++)
318 newVector
[i
].clear();
321 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
322 newButterfly
->arrayStorage()->m_indexBias
= newIndexBias
;
323 setButterflyWithoutChangingStructure(vm
, newButterfly
);
328 bool JSArray::setLengthWithArrayStorage(ExecState
* exec
, unsigned newLength
, bool throwException
, ArrayStorage
* storage
)
330 unsigned length
= storage
->length();
332 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
333 ASSERT(isLengthWritable() || storage
->m_sparseMap
);
335 if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
336 // Fail if the length is not writable.
337 if (map
->lengthIsReadOnly())
338 return reject(exec
, throwException
, StrictModeReadonlyPropertyWriteError
);
340 if (newLength
< length
) {
341 // Copy any keys we might be interested in into a vector.
342 Vector
<unsigned, 0, UnsafeVectorOverflow
> keys
;
343 keys
.reserveInitialCapacity(min(map
->size(), static_cast<size_t>(length
- newLength
)));
344 SparseArrayValueMap::const_iterator end
= map
->end();
345 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
346 unsigned index
= static_cast<unsigned>(it
->key
);
347 if (index
< length
&& index
>= newLength
)
351 // Check if the array is in sparse mode. If so there may be non-configurable
352 // properties, so we have to perform deletion with caution, if not we can
353 // delete values in any order.
354 if (map
->sparseMode()) {
355 qsort(keys
.begin(), keys
.size(), sizeof(unsigned), compareKeysForQSort
);
356 unsigned i
= keys
.size();
358 unsigned index
= keys
[--i
];
359 SparseArrayValueMap::iterator it
= map
->find(index
);
360 ASSERT(it
!= map
->notFound());
361 if (it
->value
.attributes
& DontDelete
) {
362 storage
->setLength(index
+ 1);
363 return reject(exec
, throwException
, "Unable to delete property.");
368 for (unsigned i
= 0; i
< keys
.size(); ++i
)
369 map
->remove(keys
[i
]);
371 deallocateSparseIndexMap();
376 if (newLength
< length
) {
377 // Delete properties from the vector.
378 unsigned usedVectorLength
= min(length
, storage
->vectorLength());
379 for (unsigned i
= newLength
; i
< usedVectorLength
; ++i
) {
380 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
381 bool hadValue
= valueSlot
;
383 storage
->m_numValuesInVector
-= hadValue
;
387 storage
->setLength(newLength
);
392 bool JSArray::setLength(ExecState
* exec
, unsigned newLength
, bool throwException
)
394 switch (indexingType()) {
398 if (newLength
>= MIN_SPARSE_ARRAY_INDEX
) {
399 return setLengthWithArrayStorage(
400 exec
, newLength
, throwException
,
401 convertContiguousToArrayStorage(exec
->vm()));
403 createInitialUndecided(exec
->vm(), newLength
);
406 case ArrayWithUndecided
:
408 case ArrayWithDouble
:
409 case ArrayWithContiguous
:
410 if (newLength
== m_butterfly
->publicLength())
412 if (newLength
>= MAX_ARRAY_INDEX
// This case ensures that we can do fast push.
413 || (newLength
>= MIN_SPARSE_ARRAY_INDEX
414 && !isDenseEnoughForVector(newLength
, countElements()))) {
415 return setLengthWithArrayStorage(
416 exec
, newLength
, throwException
,
417 ensureArrayStorage(exec
->vm()));
419 if (newLength
> m_butterfly
->publicLength()) {
420 ensureLength(exec
->vm(), newLength
);
423 if (indexingType() == ArrayWithDouble
) {
424 for (unsigned i
= m_butterfly
->publicLength(); i
-- > newLength
;)
425 m_butterfly
->contiguousDouble()[i
] = PNaN
;
427 for (unsigned i
= m_butterfly
->publicLength(); i
-- > newLength
;)
428 m_butterfly
->contiguous()[i
].clear();
430 m_butterfly
->setPublicLength(newLength
);
433 case ArrayWithArrayStorage
:
434 case ArrayWithSlowPutArrayStorage
:
435 return setLengthWithArrayStorage(exec
, newLength
, throwException
, arrayStorage());
443 JSValue
JSArray::pop(ExecState
* exec
)
445 switch (indexingType()) {
447 return jsUndefined();
449 case ArrayWithUndecided
:
450 if (!m_butterfly
->publicLength())
451 return jsUndefined();
452 // We have nothing but holes. So, drop down to the slow version.
456 case ArrayWithContiguous
: {
457 unsigned length
= m_butterfly
->publicLength();
460 return jsUndefined();
462 RELEASE_ASSERT(length
< m_butterfly
->vectorLength());
463 JSValue value
= m_butterfly
->contiguous()[length
].get();
465 m_butterfly
->contiguous()[length
].clear();
466 m_butterfly
->setPublicLength(length
);
472 case ArrayWithDouble
: {
473 unsigned length
= m_butterfly
->publicLength();
476 return jsUndefined();
478 RELEASE_ASSERT(length
< m_butterfly
->vectorLength());
479 double value
= m_butterfly
->contiguousDouble()[length
];
480 if (value
== value
) {
481 m_butterfly
->contiguousDouble()[length
] = PNaN
;
482 m_butterfly
->setPublicLength(length
);
483 return JSValue(JSValue::EncodeAsDouble
, value
);
488 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
: {
489 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
491 unsigned length
= storage
->length();
493 if (!isLengthWritable())
494 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
495 return jsUndefined();
498 unsigned index
= length
- 1;
499 if (index
< storage
->vectorLength()) {
500 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[index
];
502 --storage
->m_numValuesInVector
;
503 JSValue element
= valueSlot
.get();
506 RELEASE_ASSERT(isLengthWritable());
507 storage
->setLength(index
);
519 unsigned index
= getArrayLength() - 1;
520 // Let element be the result of calling the [[Get]] internal method of O with argument indx.
521 JSValue element
= get(exec
, index
);
522 if (exec
->hadException())
523 return jsUndefined();
524 // Call the [[Delete]] internal method of O with arguments indx and true.
525 if (!deletePropertyByIndex(this, exec
, index
)) {
526 throwTypeError(exec
, "Unable to delete property.");
527 return jsUndefined();
529 // Call the [[Put]] internal method of O with arguments "length", indx, and true.
530 setLength(exec
, index
, true);
535 // Push & putIndex are almost identical, with two small differences.
536 // - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
537 // - pushing to an array of length 2^32-1 stores the property, but throws a range error.
538 void JSArray::push(ExecState
* exec
, JSValue value
)
540 switch (indexingType()) {
542 createInitialUndecided(exec
->vm(), 0);
546 case ArrayWithUndecided
: {
547 convertUndecidedForValue(exec
->vm(), value
);
552 case ArrayWithInt32
: {
553 if (!value
.isInt32()) {
554 convertInt32ForValue(exec
->vm(), value
);
559 unsigned length
= m_butterfly
->publicLength();
560 ASSERT(length
<= m_butterfly
->vectorLength());
561 if (length
< m_butterfly
->vectorLength()) {
562 m_butterfly
->contiguousInt32()[length
].setWithoutWriteBarrier(value
);
563 m_butterfly
->setPublicLength(length
+ 1);
567 if (length
> MAX_ARRAY_INDEX
) {
568 methodTable(exec
->vm())->putByIndex(this, exec
, length
, value
, true);
569 if (!exec
->hadException())
570 exec
->vm().throwException(exec
, createRangeError(exec
, "Invalid array length"));
574 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, length
, value
);
578 case ArrayWithContiguous
: {
579 unsigned length
= m_butterfly
->publicLength();
580 ASSERT(length
<= m_butterfly
->vectorLength());
581 if (length
< m_butterfly
->vectorLength()) {
582 m_butterfly
->contiguous()[length
].set(exec
->vm(), this, value
);
583 m_butterfly
->setPublicLength(length
+ 1);
587 if (length
> MAX_ARRAY_INDEX
) {
588 methodTable(exec
->vm())->putByIndex(this, exec
, length
, value
, true);
589 if (!exec
->hadException())
590 exec
->vm().throwException(exec
, createRangeError(exec
, "Invalid array length"));
594 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, length
, value
);
598 case ArrayWithDouble
: {
599 if (!value
.isNumber()) {
600 convertDoubleToContiguous(exec
->vm());
604 double valueAsDouble
= value
.asNumber();
605 if (valueAsDouble
!= valueAsDouble
) {
606 convertDoubleToContiguous(exec
->vm());
611 unsigned length
= m_butterfly
->publicLength();
612 ASSERT(length
<= m_butterfly
->vectorLength());
613 if (length
< m_butterfly
->vectorLength()) {
614 m_butterfly
->contiguousDouble()[length
] = valueAsDouble
;
615 m_butterfly
->setPublicLength(length
+ 1);
619 if (length
> MAX_ARRAY_INDEX
) {
620 methodTable(exec
->vm())->putByIndex(this, exec
, length
, value
, true);
621 if (!exec
->hadException())
622 exec
->vm().throwException(exec
, createRangeError(exec
, "Invalid array length"));
626 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, length
, value
);
630 case ArrayWithSlowPutArrayStorage
: {
631 unsigned oldLength
= length();
632 if (attemptToInterceptPutByIndexOnHole(exec
, oldLength
, value
, true)) {
633 if (!exec
->hadException() && oldLength
< 0xFFFFFFFFu
)
634 setLength(exec
, oldLength
+ 1, true);
640 case ArrayWithArrayStorage
: {
641 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
643 // Fast case - push within vector, always update m_length & m_numValuesInVector.
644 unsigned length
= storage
->length();
645 if (length
< storage
->vectorLength()) {
646 storage
->m_vector
[length
].set(exec
->vm(), this, value
);
647 storage
->setLength(length
+ 1);
648 ++storage
->m_numValuesInVector
;
652 // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
653 if (storage
->length() > MAX_ARRAY_INDEX
) {
654 methodTable(exec
->vm())->putByIndex(this, exec
, storage
->length(), value
, true);
655 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
656 if (!exec
->hadException())
657 exec
->vm().throwException(exec
, createRangeError(exec
, "Invalid array length"));
661 // Handled the same as putIndex.
662 putByIndexBeyondVectorLengthWithArrayStorage(exec
, storage
->length(), value
, true, storage
);
667 RELEASE_ASSERT_NOT_REACHED();
671 bool JSArray::shiftCountWithArrayStorage(VM
& vm
, unsigned startIndex
, unsigned count
, ArrayStorage
* storage
)
673 unsigned oldLength
= storage
->length();
674 RELEASE_ASSERT(count
<= oldLength
);
676 // If the array contains holes or is otherwise in an abnormal state,
677 // use the generic algorithm in ArrayPrototype.
678 if ((storage
->hasHoles() && this->structure(vm
)->holesMustForwardToPrototype(vm
))
679 || inSparseIndexingMode()
680 || shouldUseSlowPut(indexingType())) {
687 unsigned length
= oldLength
- count
;
689 storage
->m_numValuesInVector
-= count
;
690 storage
->setLength(length
);
692 unsigned vectorLength
= storage
->vectorLength();
696 if (startIndex
>= vectorLength
)
699 if (startIndex
+ count
> vectorLength
)
700 count
= vectorLength
- startIndex
;
702 unsigned usedVectorLength
= min(vectorLength
, oldLength
);
704 unsigned numElementsBeforeShiftRegion
= startIndex
;
705 unsigned firstIndexAfterShiftRegion
= startIndex
+ count
;
706 unsigned numElementsAfterShiftRegion
= usedVectorLength
- firstIndexAfterShiftRegion
;
707 ASSERT(numElementsBeforeShiftRegion
+ count
+ numElementsAfterShiftRegion
== usedVectorLength
);
709 // The point of this comparison seems to be to minimize the amount of elements that have to
710 // be moved during a shift operation.
711 if (numElementsBeforeShiftRegion
< numElementsAfterShiftRegion
) {
712 // The number of elements before the shift region is less than the number of elements
713 // after the shift region, so we move the elements before to the right.
714 if (numElementsBeforeShiftRegion
) {
715 RELEASE_ASSERT(count
+ startIndex
<= vectorLength
);
716 if (storage
->hasHoles()) {
717 for (unsigned i
= startIndex
; i
-- > 0;) {
718 unsigned destinationIndex
= count
+ i
;
719 JSValue source
= storage
->m_vector
[i
].get();
720 JSValue dest
= storage
->m_vector
[destinationIndex
].get();
721 // Any time we overwrite a hole we know we overcounted the number of values we removed
722 // when we subtracted count from m_numValuesInVector above.
723 if (!dest
&& destinationIndex
>= firstIndexAfterShiftRegion
)
724 storage
->m_numValuesInVector
++;
725 storage
->m_vector
[count
+ i
].setWithoutWriteBarrier(source
);
728 memmove(storage
->m_vector
+ count
,
730 sizeof(JSValue
) * startIndex
);
733 // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
734 // the start of the Butterfly, which needs to point at the first indexed property in the used
735 // portion of the vector.
736 m_butterfly
.setWithoutWriteBarrier(m_butterfly
->shift(structure(), count
));
737 storage
= m_butterfly
->arrayStorage();
738 storage
->m_indexBias
+= count
;
740 // Since we're consuming part of the vector by moving its beginning to the left,
741 // we need to modify the vector length appropriately.
742 storage
->setVectorLength(vectorLength
- count
);
744 // The number of elements before the shift region is greater than or equal to the number
745 // of elements after the shift region, so we move the elements after the shift region to the left.
746 if (storage
->hasHoles()) {
747 for (unsigned i
= 0; i
< numElementsAfterShiftRegion
; ++i
) {
748 unsigned destinationIndex
= startIndex
+ i
;
749 JSValue source
= storage
->m_vector
[firstIndexAfterShiftRegion
+ i
].get();
750 JSValue dest
= storage
->m_vector
[destinationIndex
].get();
751 // Any time we overwrite a hole we know we overcounted the number of values we removed
752 // when we subtracted count from m_numValuesInVector above.
753 if (!dest
&& destinationIndex
< firstIndexAfterShiftRegion
)
754 storage
->m_numValuesInVector
++;
755 storage
->m_vector
[startIndex
+ i
].setWithoutWriteBarrier(source
);
758 memmove(storage
->m_vector
+ startIndex
,
759 storage
->m_vector
+ firstIndexAfterShiftRegion
,
760 sizeof(JSValue
) * numElementsAfterShiftRegion
);
762 // Clear the slots of the elements we just moved.
763 unsigned startOfEmptyVectorTail
= usedVectorLength
- count
;
764 for (unsigned i
= startOfEmptyVectorTail
; i
< usedVectorLength
; ++i
)
765 storage
->m_vector
[i
].clear();
766 // We don't modify the index bias or the Butterfly pointer in this case because we're not changing
767 // the start of the Butterfly, which needs to point at the first indexed property in the used
768 // portion of the vector. We also don't modify the vector length because we're not actually changing
769 // its length; we're just using less of it.
775 bool JSArray::shiftCountWithAnyIndexingType(ExecState
* exec
, unsigned& startIndex
, unsigned count
)
778 RELEASE_ASSERT(count
> 0);
780 switch (indexingType()) {
784 case ArrayWithUndecided
:
785 // Don't handle this because it's confusing and it shouldn't come up.
789 case ArrayWithContiguous
: {
790 unsigned oldLength
= m_butterfly
->publicLength();
791 RELEASE_ASSERT(count
<= oldLength
);
793 // We may have to walk the entire array to do the shift. We're willing to do
794 // so only if it's not horribly slow.
795 if (oldLength
- (startIndex
+ count
) >= MIN_SPARSE_ARRAY_INDEX
)
796 return shiftCountWithArrayStorage(vm
, startIndex
, count
, ensureArrayStorage(vm
));
798 // Storing to a hole is fine since we're still having a good time. But reading from a hole
799 // is totally not fine, since we might have to read from the proto chain.
800 // We have to check for holes before we start moving things around so that we don't get halfway
801 // through shifting and then realize we should have been in ArrayStorage mode.
802 unsigned end
= oldLength
- count
;
803 if (this->structure(vm
)->holesMustForwardToPrototype(vm
)) {
804 for (unsigned i
= startIndex
; i
< end
; ++i
) {
805 JSValue v
= m_butterfly
->contiguous()[i
+ count
].get();
808 return shiftCountWithArrayStorage(vm
, startIndex
, count
, ensureArrayStorage(vm
));
810 m_butterfly
->contiguous()[i
].setWithoutWriteBarrier(v
);
813 memmove(m_butterfly
->contiguous().data() + startIndex
,
814 m_butterfly
->contiguous().data() + startIndex
+ count
,
815 sizeof(JSValue
) * (end
- startIndex
));
818 for (unsigned i
= end
; i
< oldLength
; ++i
)
819 m_butterfly
->contiguous()[i
].clear();
821 m_butterfly
->setPublicLength(oldLength
- count
);
825 case ArrayWithDouble
: {
826 unsigned oldLength
= m_butterfly
->publicLength();
827 RELEASE_ASSERT(count
<= oldLength
);
829 // We may have to walk the entire array to do the shift. We're willing to do
830 // so only if it's not horribly slow.
831 if (oldLength
- (startIndex
+ count
) >= MIN_SPARSE_ARRAY_INDEX
)
832 return shiftCountWithArrayStorage(vm
, startIndex
, count
, ensureArrayStorage(vm
));
834 // Storing to a hole is fine since we're still having a good time. But reading from a hole
835 // is totally not fine, since we might have to read from the proto chain.
836 // We have to check for holes before we start moving things around so that we don't get halfway
837 // through shifting and then realize we should have been in ArrayStorage mode.
838 unsigned end
= oldLength
- count
;
839 if (this->structure(vm
)->holesMustForwardToPrototype(vm
)) {
840 for (unsigned i
= startIndex
; i
< end
; ++i
) {
841 double v
= m_butterfly
->contiguousDouble()[i
+ count
];
842 if (UNLIKELY(v
!= v
)) {
844 return shiftCountWithArrayStorage(vm
, startIndex
, count
, ensureArrayStorage(vm
));
846 m_butterfly
->contiguousDouble()[i
] = v
;
849 memmove(m_butterfly
->contiguousDouble().data() + startIndex
,
850 m_butterfly
->contiguousDouble().data() + startIndex
+ count
,
851 sizeof(JSValue
) * (end
- startIndex
));
853 for (unsigned i
= end
; i
< oldLength
; ++i
)
854 m_butterfly
->contiguousDouble()[i
] = PNaN
;
856 m_butterfly
->setPublicLength(oldLength
- count
);
860 case ArrayWithArrayStorage
:
861 case ArrayWithSlowPutArrayStorage
:
862 return shiftCountWithArrayStorage(vm
, startIndex
, count
, arrayStorage());
870 // Returns true if the unshift can be handled, false to fallback.
871 bool JSArray::unshiftCountWithArrayStorage(ExecState
* exec
, unsigned startIndex
, unsigned count
, ArrayStorage
* storage
)
873 unsigned length
= storage
->length();
875 RELEASE_ASSERT(startIndex
<= length
);
877 // If the array contains holes or is otherwise in an abnormal state,
878 // use the generic algorithm in ArrayPrototype.
879 if (storage
->hasHoles() || storage
->inSparseMode() || shouldUseSlowPut(indexingType()))
882 bool moveFront
= !startIndex
|| startIndex
< length
/ 2;
884 unsigned vectorLength
= storage
->vectorLength();
886 if (moveFront
&& storage
->m_indexBias
>= count
) {
887 Butterfly
* newButterfly
= storage
->butterfly()->unshift(structure(), count
);
888 storage
= newButterfly
->arrayStorage();
889 storage
->m_indexBias
-= count
;
890 storage
->setVectorLength(vectorLength
+ count
);
891 setButterflyWithoutChangingStructure(exec
->vm(), newButterfly
);
892 } else if (!moveFront
&& vectorLength
- length
>= count
)
893 storage
= storage
->butterfly()->arrayStorage();
894 else if (unshiftCountSlowCase(exec
->vm(), moveFront
, count
))
895 storage
= arrayStorage();
897 throwOutOfMemoryError(exec
);
901 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
905 memmove(vector
, vector
+ count
, startIndex
* sizeof(JSValue
));
906 else if (length
- startIndex
)
907 memmove(vector
+ startIndex
+ count
, vector
+ startIndex
, (length
- startIndex
) * sizeof(JSValue
));
910 for (unsigned i
= 0; i
< count
; i
++)
911 vector
[i
+ startIndex
].clear();
915 bool JSArray::unshiftCountWithAnyIndexingType(ExecState
* exec
, unsigned startIndex
, unsigned count
)
917 switch (indexingType()) {
919 case ArrayWithUndecided
:
920 // We could handle this. But it shouldn't ever come up, so we won't.
924 case ArrayWithContiguous
: {
925 unsigned oldLength
= m_butterfly
->publicLength();
927 // We may have to walk the entire array to do the unshift. We're willing to do so
928 // only if it's not horribly slow.
929 if (oldLength
- startIndex
>= MIN_SPARSE_ARRAY_INDEX
)
930 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
932 ensureLength(exec
->vm(), oldLength
+ count
);
934 // We have to check for holes before we start moving things around so that we don't get halfway
935 // through shifting and then realize we should have been in ArrayStorage mode.
936 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
937 JSValue v
= m_butterfly
->contiguous()[i
].get();
939 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
942 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
943 JSValue v
= m_butterfly
->contiguous()[i
].get();
945 m_butterfly
->contiguous()[i
+ count
].setWithoutWriteBarrier(v
);
948 // NOTE: we're leaving being garbage in the part of the array that we shifted out
949 // of. This is fine because the caller is required to store over that area, and
950 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
951 // as storing over an existing element.
956 case ArrayWithDouble
: {
957 unsigned oldLength
= m_butterfly
->publicLength();
959 // We may have to walk the entire array to do the unshift. We're willing to do so
960 // only if it's not horribly slow.
961 if (oldLength
- startIndex
>= MIN_SPARSE_ARRAY_INDEX
)
962 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
964 ensureLength(exec
->vm(), oldLength
+ count
);
966 // We have to check for holes before we start moving things around so that we don't get halfway
967 // through shifting and then realize we should have been in ArrayStorage mode.
968 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
969 double v
= m_butterfly
->contiguousDouble()[i
];
970 if (UNLIKELY(v
!= v
))
971 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
974 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
975 double v
= m_butterfly
->contiguousDouble()[i
];
977 m_butterfly
->contiguousDouble()[i
+ count
] = v
;
980 // NOTE: we're leaving being garbage in the part of the array that we shifted out
981 // of. This is fine because the caller is required to store over that area, and
982 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
983 // as storing over an existing element.
988 case ArrayWithArrayStorage
:
989 case ArrayWithSlowPutArrayStorage
:
990 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, arrayStorage());
998 static int compareNumbersForQSortWithInt32(const void* a
, const void* b
)
1000 int32_t ia
= static_cast<const JSValue
*>(a
)->asInt32();
1001 int32_t ib
= static_cast<const JSValue
*>(b
)->asInt32();
1005 static int compareNumbersForQSortWithDouble(const void* a
, const void* b
)
1007 double da
= *static_cast<const double*>(a
);
1008 double db
= *static_cast<const double*>(b
);
1009 return (da
> db
) - (da
< db
);
1012 static int compareNumbersForQSort(const void* a
, const void* b
)
1014 double da
= static_cast<const JSValue
*>(a
)->asNumber();
1015 double db
= static_cast<const JSValue
*>(b
)->asNumber();
1016 return (da
> db
) - (da
< db
);
1019 static int compareByStringPairForQSort(const void* a
, const void* b
)
1021 const ValueStringPair
* va
= static_cast<const ValueStringPair
*>(a
);
1022 const ValueStringPair
* vb
= static_cast<const ValueStringPair
*>(b
);
1023 return codePointCompare(va
->second
, vb
->second
);
1026 template<IndexingType arrayIndexingType
>
1027 void JSArray::sortNumericVector(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1029 ASSERT(arrayIndexingType
== ArrayWithInt32
|| arrayIndexingType
== ArrayWithDouble
|| arrayIndexingType
== ArrayWithContiguous
|| arrayIndexingType
== ArrayWithArrayStorage
);
1031 unsigned lengthNotIncludingUndefined
;
1032 unsigned newRelevantLength
;
1033 compactForSorting
<arrayIndexingType
>(
1034 lengthNotIncludingUndefined
,
1037 ContiguousJSValues data
= indexingData
<arrayIndexingType
>();
1039 if (arrayIndexingType
== ArrayWithArrayStorage
&& arrayStorage()->m_sparseMap
.get()) {
1040 throwOutOfMemoryError(exec
);
1044 if (!lengthNotIncludingUndefined
)
1047 bool allValuesAreNumbers
= true;
1048 switch (arrayIndexingType
) {
1049 case ArrayWithInt32
:
1050 case ArrayWithDouble
:
1054 for (size_t i
= 0; i
< newRelevantLength
; ++i
) {
1055 if (!data
[i
].isNumber()) {
1056 allValuesAreNumbers
= false;
1063 if (!allValuesAreNumbers
)
1064 return sort(exec
, compareFunction
, callType
, callData
);
1066 // For numeric comparison, which is fast, qsort is faster than mergesort. We
1067 // also don't require mergesort's stability, since there's no user visible
1068 // side-effect from swapping the order of equal primitive values.
1069 int (*compare
)(const void*, const void*);
1070 switch (arrayIndexingType
) {
1071 case ArrayWithInt32
:
1072 compare
= compareNumbersForQSortWithInt32
;
1075 case ArrayWithDouble
:
1076 compare
= compareNumbersForQSortWithDouble
;
1077 ASSERT(sizeof(WriteBarrier
<Unknown
>) == sizeof(double));
1081 compare
= compareNumbersForQSort
;
1084 ASSERT(data
.length() >= newRelevantLength
);
1085 qsort(data
.data(), newRelevantLength
, sizeof(WriteBarrier
<Unknown
>), compare
);
1089 void JSArray::sortNumeric(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1091 ASSERT(!inSparseIndexingMode());
1093 switch (indexingType()) {
1097 case ArrayWithInt32
:
1098 sortNumericVector
<ArrayWithInt32
>(exec
, compareFunction
, callType
, callData
);
1101 case ArrayWithDouble
:
1102 sortNumericVector
<ArrayWithDouble
>(exec
, compareFunction
, callType
, callData
);
1105 case ArrayWithContiguous
:
1106 sortNumericVector
<ArrayWithContiguous
>(exec
, compareFunction
, callType
, callData
);
1109 case ArrayWithArrayStorage
:
1110 sortNumericVector
<ArrayWithArrayStorage
>(exec
, compareFunction
, callType
, callData
);
1119 template <IndexingType
> struct ContiguousTypeAccessor
{
1120 typedef WriteBarrier
<Unknown
> Type
;
1121 static JSValue
getAsValue(ContiguousData
<Type
> data
, size_t i
) { return data
[i
].get(); }
1122 static void setWithValue(VM
& vm
, JSArray
* thisValue
, ContiguousData
<Type
> data
, size_t i
, JSValue value
)
1124 data
[i
].set(vm
, thisValue
, value
);
1126 static void replaceDataReference(ContiguousData
<Type
>* outData
, ContiguousJSValues inData
)
1132 template <> struct ContiguousTypeAccessor
<ArrayWithDouble
> {
1133 typedef double Type
;
1134 static JSValue
getAsValue(ContiguousData
<Type
> data
, size_t i
) { ASSERT(data
[i
] == data
[i
]); return JSValue(JSValue::EncodeAsDouble
, data
[i
]); }
1135 static void setWithValue(VM
&, JSArray
*, ContiguousData
<Type
> data
, size_t i
, JSValue value
)
1137 data
[i
] = value
.asNumber();
1139 static NO_RETURN_DUE_TO_CRASH
void replaceDataReference(ContiguousData
<Type
>*, ContiguousJSValues
)
1141 RELEASE_ASSERT_WITH_MESSAGE(0, "Inconsistent indexing types during compact array sort.");
1146 template<IndexingType arrayIndexingType
, typename StorageType
>
1147 void JSArray::sortCompactedVector(ExecState
* exec
, ContiguousData
<StorageType
> data
, unsigned relevantLength
)
1149 if (!relevantLength
)
1152 VM
& vm
= exec
->vm();
1154 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
1155 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
1156 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
1157 // random or otherwise changing results, effectively making compare function inconsistent.
1159 Vector
<ValueStringPair
, 0, UnsafeVectorOverflow
> values(relevantLength
);
1160 if (!values
.begin()) {
1161 throwOutOfMemoryError(exec
);
1165 Heap::heap(this)->pushTempSortVector(&values
);
1167 bool isSortingPrimitiveValues
= true;
1169 for (size_t i
= 0; i
< relevantLength
; i
++) {
1170 JSValue value
= ContiguousTypeAccessor
<arrayIndexingType
>::getAsValue(data
, i
);
1171 ASSERT(arrayIndexingType
!= ArrayWithInt32
|| value
.isInt32());
1172 ASSERT(!value
.isUndefined());
1173 values
[i
].first
= value
;
1174 if (arrayIndexingType
!= ArrayWithDouble
&& arrayIndexingType
!= ArrayWithInt32
)
1175 isSortingPrimitiveValues
= isSortingPrimitiveValues
&& value
.isPrimitive();
1178 // FIXME: The following loop continues to call toString on subsequent values even after
1179 // a toString call raises an exception.
1181 for (size_t i
= 0; i
< relevantLength
; i
++)
1182 values
[i
].second
= values
[i
].first
.toWTFStringInline(exec
);
1184 if (exec
->hadException()) {
1185 Heap::heap(this)->popTempSortVector(&values
);
1189 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
1193 if (isSortingPrimitiveValues
)
1194 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1196 mergesort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1198 // FIXME: The qsort library function is likely to not be a stable sort.
1199 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
1200 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1203 // If the toString function changed the length of the array or vector storage,
1204 // increase the length to handle the orignal number of actual values.
1205 switch (arrayIndexingType
) {
1206 case ArrayWithInt32
:
1207 case ArrayWithDouble
:
1208 case ArrayWithContiguous
:
1209 ensureLength(vm
, relevantLength
);
1212 case ArrayWithArrayStorage
:
1213 if (arrayStorage()->vectorLength() < relevantLength
) {
1214 increaseVectorLength(exec
->vm(), relevantLength
);
1215 ContiguousTypeAccessor
<arrayIndexingType
>::replaceDataReference(&data
, arrayStorage()->vector());
1217 if (arrayStorage()->length() < relevantLength
)
1218 arrayStorage()->setLength(relevantLength
);
1225 for (size_t i
= 0; i
< relevantLength
; i
++)
1226 ContiguousTypeAccessor
<arrayIndexingType
>::setWithValue(vm
, this, data
, i
, values
[i
].first
);
1228 Heap::heap(this)->popTempSortVector(&values
);
1231 void JSArray::sort(ExecState
* exec
)
1233 ASSERT(!inSparseIndexingMode());
1235 switch (indexingType()) {
1237 case ArrayWithUndecided
:
1240 case ArrayWithInt32
: {
1241 unsigned lengthNotIncludingUndefined
;
1242 unsigned newRelevantLength
;
1243 compactForSorting
<ArrayWithInt32
>(
1244 lengthNotIncludingUndefined
, newRelevantLength
);
1246 sortCompactedVector
<ArrayWithInt32
>(
1247 exec
, m_butterfly
->contiguousInt32(), lengthNotIncludingUndefined
);
1251 case ArrayWithDouble
: {
1252 unsigned lengthNotIncludingUndefined
;
1253 unsigned newRelevantLength
;
1254 compactForSorting
<ArrayWithDouble
>(
1255 lengthNotIncludingUndefined
, newRelevantLength
);
1257 sortCompactedVector
<ArrayWithDouble
>(
1258 exec
, m_butterfly
->contiguousDouble(), lengthNotIncludingUndefined
);
1262 case ArrayWithContiguous
: {
1263 unsigned lengthNotIncludingUndefined
;
1264 unsigned newRelevantLength
;
1265 compactForSorting
<ArrayWithContiguous
>(
1266 lengthNotIncludingUndefined
, newRelevantLength
);
1268 sortCompactedVector
<ArrayWithContiguous
>(
1269 exec
, m_butterfly
->contiguous(), lengthNotIncludingUndefined
);
1273 case ArrayWithArrayStorage
: {
1274 unsigned lengthNotIncludingUndefined
;
1275 unsigned newRelevantLength
;
1276 compactForSorting
<ArrayWithArrayStorage
>(
1277 lengthNotIncludingUndefined
, newRelevantLength
);
1278 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
1279 ASSERT(!storage
->m_sparseMap
);
1281 sortCompactedVector
<ArrayWithArrayStorage
>(exec
, storage
->vector(), lengthNotIncludingUndefined
);
1286 RELEASE_ASSERT_NOT_REACHED();
1290 struct AVLTreeNodeForArrayCompare
{
1293 // Child pointers. The high bit of gt is robbed and used as the
1294 // balance factor sign. The high bit of lt is robbed and used as
1295 // the magnitude of the balance factor.
1300 struct AVLTreeAbstractorForArrayCompare
{
1301 typedef int32_t handle
; // Handle is an index into m_nodes vector.
1302 typedef JSValue key
;
1303 typedef int32_t size
;
1305 Vector
<AVLTreeNodeForArrayCompare
, 0, UnsafeVectorOverflow
> m_nodes
;
1307 JSValue m_compareFunction
;
1308 CallType m_compareCallType
;
1309 const CallData
* m_compareCallData
;
1310 OwnPtr
<CachedCall
> m_cachedCall
;
1312 handle
get_less(handle h
) { return m_nodes
[h
].lt
& 0x7FFFFFFF; }
1313 void set_less(handle h
, handle lh
) { m_nodes
[h
].lt
&= 0x80000000; m_nodes
[h
].lt
|= lh
; }
1314 handle
get_greater(handle h
) { return m_nodes
[h
].gt
& 0x7FFFFFFF; }
1315 void set_greater(handle h
, handle gh
) { m_nodes
[h
].gt
&= 0x80000000; m_nodes
[h
].gt
|= gh
; }
1317 int get_balance_factor(handle h
)
1319 if (m_nodes
[h
].gt
& 0x80000000)
1321 return static_cast<unsigned>(m_nodes
[h
].lt
) >> 31;
1324 void set_balance_factor(handle h
, int bf
)
1327 m_nodes
[h
].lt
&= 0x7FFFFFFF;
1328 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1330 m_nodes
[h
].lt
|= 0x80000000;
1332 m_nodes
[h
].gt
|= 0x80000000;
1334 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1338 int compare_key_key(key va
, key vb
)
1340 ASSERT(!va
.isUndefined());
1341 ASSERT(!vb
.isUndefined());
1343 if (m_exec
->hadException())
1346 double compareResult
;
1348 m_cachedCall
->setThis(jsUndefined());
1349 m_cachedCall
->setArgument(0, va
);
1350 m_cachedCall
->setArgument(1, vb
);
1351 compareResult
= m_cachedCall
->call().toNumber(m_exec
);
1353 MarkedArgumentBuffer arguments
;
1354 arguments
.append(va
);
1355 arguments
.append(vb
);
1356 compareResult
= call(m_exec
, m_compareFunction
, m_compareCallType
, *m_compareCallData
, jsUndefined(), arguments
).toNumber(m_exec
);
1358 return (compareResult
< 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1361 int compare_key_node(key k
, handle h
) { return compare_key_key(k
, m_nodes
[h
].value
); }
1362 int compare_node_node(handle h1
, handle h2
) { return compare_key_key(m_nodes
[h1
].value
, m_nodes
[h2
].value
); }
1364 static handle
null() { return 0x7FFFFFFF; }
1367 template<IndexingType arrayIndexingType
>
1368 void JSArray::sortVector(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1370 ASSERT(!inSparseIndexingMode());
1371 ASSERT(arrayIndexingType
== indexingType());
1373 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1375 // The maximum tree depth is compiled in - but the caller is clearly up to no good
1376 // if a larger array is passed.
1377 ASSERT(m_butterfly
->publicLength() <= static_cast<unsigned>(std::numeric_limits
<int>::max()));
1378 if (m_butterfly
->publicLength() > static_cast<unsigned>(std::numeric_limits
<int>::max()))
1381 unsigned usedVectorLength
= relevantLength
<arrayIndexingType
>();
1382 unsigned nodeCount
= usedVectorLength
;
1387 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44> tree
; // Depth 44 is enough for 2^31 items
1388 tree
.abstractor().m_exec
= exec
;
1389 tree
.abstractor().m_compareFunction
= compareFunction
;
1390 tree
.abstractor().m_compareCallType
= callType
;
1391 tree
.abstractor().m_compareCallData
= &callData
;
1392 tree
.abstractor().m_nodes
.grow(nodeCount
);
1394 if (callType
== CallTypeJS
)
1395 tree
.abstractor().m_cachedCall
= adoptPtr(new CachedCall(exec
, jsCast
<JSFunction
*>(compareFunction
), 2));
1397 if (!tree
.abstractor().m_nodes
.begin()) {
1398 throwOutOfMemoryError(exec
);
1402 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1403 // right out from under us while we're building the tree here.
1405 unsigned numDefined
= 0;
1406 unsigned numUndefined
= 0;
1408 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1409 for (; numDefined
< usedVectorLength
; ++numDefined
) {
1410 if (numDefined
>= m_butterfly
->vectorLength())
1412 JSValue v
= getHolyIndexQuickly(numDefined
);
1413 if (!v
|| v
.isUndefined())
1415 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1416 tree
.insert(numDefined
);
1418 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
1419 if (i
>= m_butterfly
->vectorLength())
1421 JSValue v
= getHolyIndexQuickly(i
);
1423 if (v
.isUndefined())
1426 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1427 tree
.insert(numDefined
);
1433 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1435 // The array size may have changed. Figure out the new bounds.
1436 unsigned newestUsedVectorLength
= currentRelevantLength();
1438 unsigned elementsToExtractThreshold
= min(min(newestUsedVectorLength
, numDefined
), static_cast<unsigned>(tree
.abstractor().m_nodes
.size()));
1439 unsigned undefinedElementsThreshold
= min(newestUsedVectorLength
, newUsedVectorLength
);
1440 unsigned clearElementsThreshold
= min(newestUsedVectorLength
, usedVectorLength
);
1442 // Copy the values back into m_storage.
1443 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44>::Iterator iter
;
1444 iter
.start_iter_least(tree
);
1445 VM
& vm
= exec
->vm();
1446 for (unsigned i
= 0; i
< elementsToExtractThreshold
; ++i
) {
1447 ASSERT(i
< butterfly()->vectorLength());
1448 if (indexingType() == ArrayWithDouble
)
1449 butterfly()->contiguousDouble()[i
] = tree
.abstractor().m_nodes
[*iter
].value
.asNumber();
1451 currentIndexingData()[i
].set(vm
, this, tree
.abstractor().m_nodes
[*iter
].value
);
1454 // Put undefined values back in.
1455 switch (indexingType()) {
1456 case ArrayWithInt32
:
1457 case ArrayWithDouble
:
1458 ASSERT(elementsToExtractThreshold
== undefinedElementsThreshold
);
1462 for (unsigned i
= elementsToExtractThreshold
; i
< undefinedElementsThreshold
; ++i
) {
1463 ASSERT(i
< butterfly()->vectorLength());
1464 currentIndexingData()[i
].setUndefined();
1468 // Ensure that unused values in the vector are zeroed out.
1469 for (unsigned i
= undefinedElementsThreshold
; i
< clearElementsThreshold
; ++i
) {
1470 ASSERT(i
< butterfly()->vectorLength());
1471 if (indexingType() == ArrayWithDouble
)
1472 butterfly()->contiguousDouble()[i
] = PNaN
;
1474 currentIndexingData()[i
].clear();
1477 if (hasAnyArrayStorage(indexingType()))
1478 arrayStorage()->m_numValuesInVector
= newUsedVectorLength
;
1481 void JSArray::sort(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1483 ASSERT(!inSparseIndexingMode());
1485 switch (indexingType()) {
1487 case ArrayWithUndecided
:
1490 case ArrayWithInt32
:
1491 sortVector
<ArrayWithInt32
>(exec
, compareFunction
, callType
, callData
);
1494 case ArrayWithDouble
:
1495 sortVector
<ArrayWithDouble
>(exec
, compareFunction
, callType
, callData
);
1498 case ArrayWithContiguous
:
1499 sortVector
<ArrayWithContiguous
>(exec
, compareFunction
, callType
, callData
);
1502 case ArrayWithArrayStorage
:
1503 sortVector
<ArrayWithArrayStorage
>(exec
, compareFunction
, callType
, callData
);
1507 RELEASE_ASSERT_NOT_REACHED();
1511 void JSArray::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
1515 WriteBarrier
<Unknown
>* vector
;
1517 switch (indexingType()) {
1521 case ArrayWithUndecided
: {
1527 case ArrayWithInt32
:
1528 case ArrayWithContiguous
: {
1529 vectorEnd
= m_butterfly
->publicLength();
1530 vector
= m_butterfly
->contiguous().data();
1534 case ArrayWithDouble
: {
1537 for (; i
< m_butterfly
->publicLength(); ++i
) {
1538 double v
= butterfly()->contiguousDouble()[i
];
1541 args
.append(JSValue(JSValue::EncodeAsDouble
, v
));
1546 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
: {
1547 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
1549 vector
= storage
->m_vector
;
1550 vectorEnd
= min(storage
->length(), storage
->vectorLength());
1561 for (; i
< vectorEnd
; ++i
) {
1562 WriteBarrier
<Unknown
>& v
= vector
[i
];
1565 args
.append(v
.get());
1568 for (; i
< length(); ++i
)
1569 args
.append(get(exec
, i
));
1572 void JSArray::copyToArguments(ExecState
* exec
, CallFrame
* callFrame
, uint32_t copyLength
, int32_t firstVarArgOffset
)
1574 unsigned i
= firstVarArgOffset
;
1575 WriteBarrier
<Unknown
>* vector
;
1577 unsigned length
= copyLength
+ firstVarArgOffset
;
1578 ASSERT(length
== this->length());
1579 switch (indexingType()) {
1583 case ArrayWithUndecided
: {
1589 case ArrayWithInt32
:
1590 case ArrayWithContiguous
: {
1591 vector
= m_butterfly
->contiguous().data();
1592 vectorEnd
= m_butterfly
->publicLength();
1596 case ArrayWithDouble
: {
1599 for (; i
< m_butterfly
->publicLength(); ++i
) {
1600 ASSERT(i
< butterfly()->vectorLength());
1601 double v
= m_butterfly
->contiguousDouble()[i
];
1604 callFrame
->setArgument(i
- firstVarArgOffset
, JSValue(JSValue::EncodeAsDouble
, v
));
1609 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
: {
1610 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
1611 vector
= storage
->m_vector
;
1612 vectorEnd
= min(length
, storage
->vectorLength());
1623 for (; i
< vectorEnd
; ++i
) {
1624 WriteBarrier
<Unknown
>& v
= vector
[i
];
1627 callFrame
->setArgument(i
- firstVarArgOffset
, v
.get());
1630 for (; i
< length
; ++i
)
1631 callFrame
->setArgument(i
- firstVarArgOffset
, get(exec
, i
));
1634 template<IndexingType arrayIndexingType
>
1635 void JSArray::compactForSorting(unsigned& numDefined
, unsigned& newRelevantLength
)
1637 ASSERT(!inSparseIndexingMode());
1638 ASSERT(arrayIndexingType
== indexingType());
1640 unsigned myRelevantLength
= relevantLength
<arrayIndexingType
>();
1643 unsigned numUndefined
= 0;
1645 for (; numDefined
< myRelevantLength
; ++numDefined
) {
1646 ASSERT(numDefined
< m_butterfly
->vectorLength());
1647 if (arrayIndexingType
== ArrayWithInt32
) {
1648 JSValue v
= m_butterfly
->contiguousInt32()[numDefined
].get();
1651 ASSERT(v
.isInt32());
1654 if (arrayIndexingType
== ArrayWithDouble
) {
1655 double v
= m_butterfly
->contiguousDouble()[numDefined
];
1660 JSValue v
= indexingData
<arrayIndexingType
>()[numDefined
].get();
1661 if (!v
|| v
.isUndefined())
1665 for (unsigned i
= numDefined
; i
< myRelevantLength
; ++i
) {
1666 ASSERT(i
< m_butterfly
->vectorLength());
1667 if (arrayIndexingType
== ArrayWithInt32
) {
1668 JSValue v
= m_butterfly
->contiguousInt32()[i
].get();
1671 ASSERT(v
.isInt32());
1672 ASSERT(numDefined
< m_butterfly
->vectorLength());
1673 m_butterfly
->contiguousInt32()[numDefined
++].setWithoutWriteBarrier(v
);
1676 if (arrayIndexingType
== ArrayWithDouble
) {
1677 double v
= m_butterfly
->contiguousDouble()[i
];
1680 ASSERT(numDefined
< m_butterfly
->vectorLength());
1681 m_butterfly
->contiguousDouble()[numDefined
++] = v
;
1684 JSValue v
= indexingData
<arrayIndexingType
>()[i
].get();
1686 if (v
.isUndefined())
1689 ASSERT(numDefined
< m_butterfly
->vectorLength());
1690 indexingData
<arrayIndexingType
>()[numDefined
++].setWithoutWriteBarrier(v
);
1695 newRelevantLength
= numDefined
+ numUndefined
;
1697 if (hasAnyArrayStorage(arrayIndexingType
))
1698 RELEASE_ASSERT(!arrayStorage()->m_sparseMap
);
1700 switch (arrayIndexingType
) {
1701 case ArrayWithInt32
:
1702 case ArrayWithDouble
:
1703 RELEASE_ASSERT(numDefined
== newRelevantLength
);
1707 for (unsigned i
= numDefined
; i
< newRelevantLength
; ++i
) {
1708 ASSERT(i
< m_butterfly
->vectorLength());
1709 indexingData
<arrayIndexingType
>()[i
].setUndefined();
1713 for (unsigned i
= newRelevantLength
; i
< myRelevantLength
; ++i
) {
1714 ASSERT(i
< m_butterfly
->vectorLength());
1715 if (arrayIndexingType
== ArrayWithDouble
)
1716 m_butterfly
->contiguousDouble()[i
] = PNaN
;
1718 indexingData
<arrayIndexingType
>()[i
].clear();
1721 if (hasAnyArrayStorage(arrayIndexingType
))
1722 arrayStorage()->m_numValuesInVector
= newRelevantLength
;