2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009, 2012 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"
30 #include "CopiedSpaceInlines.h"
32 #include "Executable.h"
33 #include "GetterSetter.h"
34 #include "IndexingHeaderInlines.h"
35 #include "PropertyNameArray.h"
37 #include <wtf/AVLTree.h>
38 #include <wtf/Assertions.h>
39 #include <wtf/OwnPtr.h>
40 #include <Operations.h>
47 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray
);
49 const ClassInfo
JSArray::s_info
= {"Array", &JSNonFinalObject::s_info
, 0, 0, CREATE_METHOD_TABLE(JSArray
)};
51 Butterfly
* createArrayButterflyInDictionaryIndexingMode(VM
& vm
, unsigned initialLength
)
53 Butterfly
* butterfly
= Butterfly::create(
54 vm
, 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
, 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 throwError(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(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
181 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
182 if (propertyName
== exec
->propertyNames().length
) {
183 slot
.setValue(jsNumber(thisObject
->length()));
187 return JSObject::getOwnPropertySlot(thisObject
, exec
, propertyName
, slot
);
190 bool JSArray::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
192 JSArray
* thisObject
= jsCast
<JSArray
*>(object
);
193 if (propertyName
== exec
->propertyNames().length
) {
194 descriptor
.setDescriptor(jsNumber(thisObject
->length()), thisObject
->isLengthWritable() ? DontDelete
| DontEnum
: DontDelete
| DontEnum
| ReadOnly
);
198 return JSObject::getOwnPropertyDescriptor(thisObject
, exec
, propertyName
, descriptor
);
202 void JSArray::put(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, JSValue value
, PutPropertySlot
& slot
)
204 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
206 if (propertyName
== exec
->propertyNames().length
) {
207 unsigned newLength
= value
.toUInt32(exec
);
208 if (value
.toNumber(exec
) != static_cast<double>(newLength
)) {
209 throwError(exec
, createRangeError(exec
, ASCIILiteral("Invalid array length")));
212 thisObject
->setLength(exec
, newLength
, slot
.isStrictMode());
216 JSObject::put(thisObject
, exec
, propertyName
, value
, slot
);
219 bool JSArray::deleteProperty(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
)
221 JSArray
* thisObject
= jsCast
<JSArray
*>(cell
);
223 if (propertyName
== exec
->propertyNames().length
)
226 return JSObject::deleteProperty(thisObject
, exec
, propertyName
);
229 static int compareKeysForQSort(const void* a
, const void* b
)
231 unsigned da
= *static_cast<const unsigned*>(a
);
232 unsigned db
= *static_cast<const unsigned*>(b
);
233 return (da
> db
) - (da
< db
);
236 void JSArray::getOwnNonIndexPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
238 JSArray
* thisObject
= jsCast
<JSArray
*>(object
);
240 if (mode
== IncludeDontEnumProperties
)
241 propertyNames
.add(exec
->propertyNames().length
);
243 JSObject::getOwnNonIndexPropertyNames(thisObject
, exec
, propertyNames
, mode
);
246 // This method makes room in the vector, but leaves the new space for count slots uncleared.
247 bool JSArray::unshiftCountSlowCase(VM
& vm
, bool addToFront
, unsigned count
)
249 ArrayStorage
* storage
= ensureArrayStorage(vm
);
250 Butterfly
* butterfly
= storage
->butterfly();
251 unsigned propertyCapacity
= structure()->outOfLineCapacity();
252 unsigned propertySize
= structure()->outOfLineSize();
254 // If not, we should have handled this on the fast path.
255 ASSERT(!addToFront
|| count
> storage
->m_indexBias
);
258 // Gather 4 key metrics:
259 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
260 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
261 // * currentCapacity - what is the current size of the vector, including any pre-capacity.
262 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
264 unsigned length
= storage
->length();
265 unsigned usedVectorLength
= min(storage
->vectorLength(), length
);
266 ASSERT(usedVectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
267 // Check that required vector length is possible, in an overflow-safe fashion.
268 if (count
> MAX_STORAGE_VECTOR_LENGTH
- usedVectorLength
)
270 unsigned requiredVectorLength
= usedVectorLength
+ count
;
271 ASSERT(requiredVectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
272 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
273 ASSERT(storage
->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH
&& (MAX_STORAGE_VECTOR_LENGTH
- storage
->vectorLength()) >= storage
->m_indexBias
);
274 unsigned currentCapacity
= storage
->vectorLength() + storage
->m_indexBias
;
275 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
276 unsigned desiredCapacity
= min(MAX_STORAGE_VECTOR_LENGTH
, max(BASE_VECTOR_LEN
, requiredVectorLength
) << 1);
279 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
281 void* newAllocBase
= 0;
282 unsigned newStorageCapacity
;
283 // If the current storage array is sufficiently large (but not too large!) then just keep using it.
284 if (currentCapacity
> desiredCapacity
&& isDenseEnoughForVector(currentCapacity
, requiredVectorLength
)) {
285 newAllocBase
= butterfly
->base(structure());
286 newStorageCapacity
= currentCapacity
;
288 size_t newSize
= Butterfly::totalSize(0, propertyCapacity
, true, ArrayStorage::sizeFor(desiredCapacity
));
289 if (!vm
.heap
.tryAllocateStorage(newSize
, &newAllocBase
))
291 newStorageCapacity
= desiredCapacity
;
295 // Work out where we're going to move things to.
297 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
298 // If we're adding to the end, we'll add all the new space to the end.
299 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
300 // If it did, we calculate the amount that will remain based on an atomic decay - leave the
301 // vector with half the post-capacity it had previously.
302 unsigned postCapacity
= 0;
304 postCapacity
= max(newStorageCapacity
- requiredVectorLength
, count
);
305 else if (length
< storage
->vectorLength()) {
306 // Atomic decay, + the post-capacity cannot be greater than what is available.
307 postCapacity
= min((storage
->vectorLength() - length
) >> 1, newStorageCapacity
- requiredVectorLength
);
308 // If we're moving contents within the same allocation, the post-capacity is being reduced.
309 ASSERT(newAllocBase
!= butterfly
->base(structure()) || postCapacity
< storage
->vectorLength() - length
);
312 unsigned newVectorLength
= requiredVectorLength
+ postCapacity
;
313 unsigned newIndexBias
= newStorageCapacity
- newVectorLength
;
315 Butterfly
* newButterfly
= Butterfly::fromBase(newAllocBase
, newIndexBias
, propertyCapacity
);
318 ASSERT(count
+ usedVectorLength
<= newVectorLength
);
319 memmove(newButterfly
->arrayStorage()->m_vector
+ count
, storage
->m_vector
, sizeof(JSValue
) * usedVectorLength
);
320 memmove(newButterfly
->propertyStorage() - propertySize
, butterfly
->propertyStorage() - propertySize
, sizeof(JSValue
) * propertySize
+ sizeof(IndexingHeader
) + ArrayStorage::sizeFor(0));
321 } else if ((newAllocBase
!= butterfly
->base(structure())) || (newIndexBias
!= storage
->m_indexBias
)) {
322 memmove(newButterfly
->propertyStorage() - propertySize
, butterfly
->propertyStorage() - propertySize
, sizeof(JSValue
) * propertySize
+ sizeof(IndexingHeader
) + ArrayStorage::sizeFor(0));
323 memmove(newButterfly
->arrayStorage()->m_vector
, storage
->m_vector
, sizeof(JSValue
) * usedVectorLength
);
325 WriteBarrier
<Unknown
>* newVector
= newButterfly
->arrayStorage()->m_vector
;
326 for (unsigned i
= requiredVectorLength
; i
< newVectorLength
; i
++)
327 newVector
[i
].clear();
330 newButterfly
->arrayStorage()->setVectorLength(newVectorLength
);
331 newButterfly
->arrayStorage()->m_indexBias
= newIndexBias
;
333 m_butterfly
= newButterfly
;
338 bool JSArray::setLengthWithArrayStorage(ExecState
* exec
, unsigned newLength
, bool throwException
, ArrayStorage
* storage
)
340 unsigned length
= storage
->length();
342 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
343 ASSERT(isLengthWritable() || storage
->m_sparseMap
);
345 if (SparseArrayValueMap
* map
= storage
->m_sparseMap
.get()) {
346 // Fail if the length is not writable.
347 if (map
->lengthIsReadOnly())
348 return reject(exec
, throwException
, StrictModeReadonlyPropertyWriteError
);
350 if (newLength
< length
) {
351 // Copy any keys we might be interested in into a vector.
352 Vector
<unsigned, 0, UnsafeVectorOverflow
> keys
;
353 keys
.reserveInitialCapacity(min(map
->size(), static_cast<size_t>(length
- newLength
)));
354 SparseArrayValueMap::const_iterator end
= map
->end();
355 for (SparseArrayValueMap::const_iterator it
= map
->begin(); it
!= end
; ++it
) {
356 unsigned index
= static_cast<unsigned>(it
->key
);
357 if (index
< length
&& index
>= newLength
)
361 // Check if the array is in sparse mode. If so there may be non-configurable
362 // properties, so we have to perform deletion with caution, if not we can
363 // delete values in any order.
364 if (map
->sparseMode()) {
365 qsort(keys
.begin(), keys
.size(), sizeof(unsigned), compareKeysForQSort
);
366 unsigned i
= keys
.size();
368 unsigned index
= keys
[--i
];
369 SparseArrayValueMap::iterator it
= map
->find(index
);
370 ASSERT(it
!= map
->notFound());
371 if (it
->value
.attributes
& DontDelete
) {
372 storage
->setLength(index
+ 1);
373 return reject(exec
, throwException
, "Unable to delete property.");
378 for (unsigned i
= 0; i
< keys
.size(); ++i
)
379 map
->remove(keys
[i
]);
381 deallocateSparseIndexMap();
386 if (newLength
< length
) {
387 // Delete properties from the vector.
388 unsigned usedVectorLength
= min(length
, storage
->vectorLength());
389 for (unsigned i
= newLength
; i
< usedVectorLength
; ++i
) {
390 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
391 bool hadValue
= valueSlot
;
393 storage
->m_numValuesInVector
-= hadValue
;
397 storage
->setLength(newLength
);
402 bool JSArray::setLength(ExecState
* exec
, unsigned newLength
, bool throwException
)
404 switch (structure()->indexingType()) {
408 if (newLength
>= MIN_SPARSE_ARRAY_INDEX
) {
409 return setLengthWithArrayStorage(
410 exec
, newLength
, throwException
,
411 convertContiguousToArrayStorage(exec
->vm()));
413 createInitialUndecided(exec
->vm(), newLength
);
416 case ArrayWithUndecided
:
418 case ArrayWithDouble
:
419 case ArrayWithContiguous
:
420 if (newLength
== m_butterfly
->publicLength())
422 if (newLength
>= MAX_ARRAY_INDEX
// This case ensures that we can do fast push.
423 || (newLength
>= MIN_SPARSE_ARRAY_INDEX
424 && !isDenseEnoughForVector(newLength
, countElements()))) {
425 return setLengthWithArrayStorage(
426 exec
, newLength
, throwException
,
427 ensureArrayStorage(exec
->vm()));
429 if (newLength
> m_butterfly
->publicLength()) {
430 ensureLength(exec
->vm(), newLength
);
433 if (structure()->indexingType() == ArrayWithDouble
) {
434 for (unsigned i
= m_butterfly
->publicLength(); i
-- > newLength
;)
435 m_butterfly
->contiguousDouble()[i
] = QNaN
;
437 for (unsigned i
= m_butterfly
->publicLength(); i
-- > newLength
;)
438 m_butterfly
->contiguous()[i
].clear();
440 m_butterfly
->setPublicLength(newLength
);
443 case ArrayWithArrayStorage
:
444 case ArrayWithSlowPutArrayStorage
:
445 return setLengthWithArrayStorage(exec
, newLength
, throwException
, arrayStorage());
453 JSValue
JSArray::pop(ExecState
* exec
)
455 switch (structure()->indexingType()) {
457 return jsUndefined();
459 case ArrayWithUndecided
:
460 if (!m_butterfly
->publicLength())
461 return jsUndefined();
462 // We have nothing but holes. So, drop down to the slow version.
466 case ArrayWithContiguous
: {
467 unsigned length
= m_butterfly
->publicLength();
470 return jsUndefined();
472 RELEASE_ASSERT(length
< m_butterfly
->vectorLength());
473 JSValue value
= m_butterfly
->contiguous()[length
].get();
475 m_butterfly
->contiguous()[length
].clear();
476 m_butterfly
->setPublicLength(length
);
482 case ArrayWithDouble
: {
483 unsigned length
= m_butterfly
->publicLength();
486 return jsUndefined();
488 RELEASE_ASSERT(length
< m_butterfly
->vectorLength());
489 double value
= m_butterfly
->contiguousDouble()[length
];
490 if (value
== value
) {
491 m_butterfly
->contiguousDouble()[length
] = QNaN
;
492 m_butterfly
->setPublicLength(length
);
493 return JSValue(JSValue::EncodeAsDouble
, value
);
498 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
: {
499 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
501 unsigned length
= storage
->length();
503 if (!isLengthWritable())
504 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
505 return jsUndefined();
508 unsigned index
= length
- 1;
509 if (index
< storage
->vectorLength()) {
510 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[index
];
512 --storage
->m_numValuesInVector
;
513 JSValue element
= valueSlot
.get();
516 RELEASE_ASSERT(isLengthWritable());
517 storage
->setLength(index
);
529 unsigned index
= getArrayLength() - 1;
530 // Let element be the result of calling the [[Get]] internal method of O with argument indx.
531 JSValue element
= get(exec
, index
);
532 if (exec
->hadException())
533 return jsUndefined();
534 // Call the [[Delete]] internal method of O with arguments indx and true.
535 if (!deletePropertyByIndex(this, exec
, index
)) {
536 throwTypeError(exec
, "Unable to delete property.");
537 return jsUndefined();
539 // Call the [[Put]] internal method of O with arguments "length", indx, and true.
540 setLength(exec
, index
, true);
545 // Push & putIndex are almost identical, with two small differences.
546 // - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
547 // - pushing to an array of length 2^32-1 stores the property, but throws a range error.
548 void JSArray::push(ExecState
* exec
, JSValue value
)
550 switch (structure()->indexingType()) {
552 createInitialUndecided(exec
->vm(), 0);
556 case ArrayWithUndecided
: {
557 convertUndecidedForValue(exec
->vm(), value
);
562 case ArrayWithInt32
: {
563 if (!value
.isInt32()) {
564 convertInt32ForValue(exec
->vm(), value
);
569 unsigned length
= m_butterfly
->publicLength();
570 ASSERT(length
<= m_butterfly
->vectorLength());
571 if (length
< m_butterfly
->vectorLength()) {
572 m_butterfly
->contiguousInt32()[length
].setWithoutWriteBarrier(value
);
573 m_butterfly
->setPublicLength(length
+ 1);
577 if (length
> MAX_ARRAY_INDEX
) {
578 methodTable()->putByIndex(this, exec
, length
, value
, true);
579 if (!exec
->hadException())
580 throwError(exec
, createRangeError(exec
, "Invalid array length"));
584 putByIndexBeyondVectorLengthWithoutAttributes
<Int32Shape
>(exec
, length
, value
);
588 case ArrayWithContiguous
: {
589 unsigned length
= m_butterfly
->publicLength();
590 ASSERT(length
<= m_butterfly
->vectorLength());
591 if (length
< m_butterfly
->vectorLength()) {
592 m_butterfly
->contiguous()[length
].set(exec
->vm(), this, value
);
593 m_butterfly
->setPublicLength(length
+ 1);
597 if (length
> MAX_ARRAY_INDEX
) {
598 methodTable()->putByIndex(this, exec
, length
, value
, true);
599 if (!exec
->hadException())
600 throwError(exec
, createRangeError(exec
, "Invalid array length"));
604 putByIndexBeyondVectorLengthWithoutAttributes
<ContiguousShape
>(exec
, length
, value
);
608 case ArrayWithDouble
: {
609 if (!value
.isNumber()) {
610 convertDoubleToContiguous(exec
->vm());
614 double valueAsDouble
= value
.asNumber();
615 if (valueAsDouble
!= valueAsDouble
) {
616 convertDoubleToContiguous(exec
->vm());
621 unsigned length
= m_butterfly
->publicLength();
622 ASSERT(length
<= m_butterfly
->vectorLength());
623 if (length
< m_butterfly
->vectorLength()) {
624 m_butterfly
->contiguousDouble()[length
] = valueAsDouble
;
625 m_butterfly
->setPublicLength(length
+ 1);
629 if (length
> MAX_ARRAY_INDEX
) {
630 methodTable()->putByIndex(this, exec
, length
, value
, true);
631 if (!exec
->hadException())
632 throwError(exec
, createRangeError(exec
, "Invalid array length"));
636 putByIndexBeyondVectorLengthWithoutAttributes
<DoubleShape
>(exec
, length
, value
);
640 case ArrayWithSlowPutArrayStorage
: {
641 unsigned oldLength
= length();
642 if (attemptToInterceptPutByIndexOnHole(exec
, oldLength
, value
, true)) {
643 if (!exec
->hadException() && oldLength
< 0xFFFFFFFFu
)
644 setLength(exec
, oldLength
+ 1, true);
650 case ArrayWithArrayStorage
: {
651 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
653 // Fast case - push within vector, always update m_length & m_numValuesInVector.
654 unsigned length
= storage
->length();
655 if (length
< storage
->vectorLength()) {
656 storage
->m_vector
[length
].set(exec
->vm(), this, value
);
657 storage
->setLength(length
+ 1);
658 ++storage
->m_numValuesInVector
;
662 // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
663 if (storage
->length() > MAX_ARRAY_INDEX
) {
664 methodTable()->putByIndex(this, exec
, storage
->length(), value
, true);
665 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
666 if (!exec
->hadException())
667 throwError(exec
, createRangeError(exec
, "Invalid array length"));
671 // Handled the same as putIndex.
672 putByIndexBeyondVectorLengthWithArrayStorage(exec
, storage
->length(), value
, true, storage
);
677 RELEASE_ASSERT_NOT_REACHED();
681 bool JSArray::shiftCountWithArrayStorage(unsigned startIndex
, unsigned count
, ArrayStorage
* storage
)
683 unsigned oldLength
= storage
->length();
684 RELEASE_ASSERT(count
<= oldLength
);
686 // If the array contains holes or is otherwise in an abnormal state,
687 // use the generic algorithm in ArrayPrototype.
688 if (oldLength
!= storage
->m_numValuesInVector
|| inSparseIndexingMode() || shouldUseSlowPut(structure()->indexingType()))
694 unsigned length
= oldLength
- count
;
696 storage
->m_numValuesInVector
-= count
;
697 storage
->setLength(length
);
699 unsigned vectorLength
= storage
->vectorLength();
703 if (startIndex
>= vectorLength
)
706 if (startIndex
+ count
> vectorLength
)
707 count
= vectorLength
- startIndex
;
709 unsigned usedVectorLength
= min(vectorLength
, oldLength
);
711 vectorLength
-= count
;
712 storage
->setVectorLength(vectorLength
);
715 if (startIndex
< usedVectorLength
- (startIndex
+ count
)) {
718 storage
->m_vector
+ count
,
720 sizeof(JSValue
) * startIndex
);
722 m_butterfly
= m_butterfly
->shift(structure(), count
);
723 storage
= m_butterfly
->arrayStorage();
724 storage
->m_indexBias
+= count
;
727 storage
->m_vector
+ startIndex
,
728 storage
->m_vector
+ startIndex
+ count
,
729 sizeof(JSValue
) * (usedVectorLength
- (startIndex
+ count
)));
730 for (unsigned i
= usedVectorLength
- count
; i
< usedVectorLength
; ++i
)
731 storage
->m_vector
[i
].clear();
737 bool JSArray::shiftCountWithAnyIndexingType(ExecState
* exec
, unsigned startIndex
, unsigned count
)
739 RELEASE_ASSERT(count
> 0);
741 switch (structure()->indexingType()) {
745 case ArrayWithUndecided
:
746 // Don't handle this because it's confusing and it shouldn't come up.
750 case ArrayWithContiguous
: {
751 unsigned oldLength
= m_butterfly
->publicLength();
752 RELEASE_ASSERT(count
<= oldLength
);
754 // We may have to walk the entire array to do the shift. We're willing to do
755 // so only if it's not horribly slow.
756 if (oldLength
- (startIndex
+ count
) >= MIN_SPARSE_ARRAY_INDEX
)
757 return shiftCountWithArrayStorage(startIndex
, count
, ensureArrayStorage(exec
->vm()));
759 // Storing to a hole is fine since we're still having a good time. But reading from a hole
760 // is totally not fine, since we might have to read from the proto chain.
761 // We have to check for holes before we start moving things around so that we don't get halfway
762 // through shifting and then realize we should have been in ArrayStorage mode.
763 unsigned end
= oldLength
- count
;
764 for (unsigned i
= startIndex
; i
< end
; ++i
) {
765 JSValue v
= m_butterfly
->contiguous()[i
+ count
].get();
767 return shiftCountWithArrayStorage(startIndex
, count
, ensureArrayStorage(exec
->vm()));
770 for (unsigned i
= startIndex
; i
< end
; ++i
) {
771 JSValue v
= m_butterfly
->contiguous()[i
+ count
].get();
773 // No need for a barrier since we're just moving data around in the same vector.
774 // This is in line with our standing assumption that we won't have a deletion
776 m_butterfly
->contiguous()[i
].setWithoutWriteBarrier(v
);
778 for (unsigned i
= end
; i
< oldLength
; ++i
)
779 m_butterfly
->contiguous()[i
].clear();
781 m_butterfly
->setPublicLength(oldLength
- count
);
785 case ArrayWithDouble
: {
786 unsigned oldLength
= m_butterfly
->publicLength();
787 RELEASE_ASSERT(count
<= oldLength
);
789 // We may have to walk the entire array to do the shift. We're willing to do
790 // so only if it's not horribly slow.
791 if (oldLength
- (startIndex
+ count
) >= MIN_SPARSE_ARRAY_INDEX
)
792 return shiftCountWithArrayStorage(startIndex
, count
, ensureArrayStorage(exec
->vm()));
794 // Storing to a hole is fine since we're still having a good time. But reading from a hole
795 // is totally not fine, since we might have to read from the proto chain.
796 // We have to check for holes before we start moving things around so that we don't get halfway
797 // through shifting and then realize we should have been in ArrayStorage mode.
798 unsigned end
= oldLength
- count
;
799 for (unsigned i
= startIndex
; i
< end
; ++i
) {
800 double v
= m_butterfly
->contiguousDouble()[i
+ count
];
801 if (UNLIKELY(v
!= v
))
802 return shiftCountWithArrayStorage(startIndex
, count
, ensureArrayStorage(exec
->vm()));
805 for (unsigned i
= startIndex
; i
< end
; ++i
) {
806 double v
= m_butterfly
->contiguousDouble()[i
+ count
];
808 // No need for a barrier since we're just moving data around in the same vector.
809 // This is in line with our standing assumption that we won't have a deletion
811 m_butterfly
->contiguousDouble()[i
] = v
;
813 for (unsigned i
= end
; i
< oldLength
; ++i
)
814 m_butterfly
->contiguousDouble()[i
] = QNaN
;
816 m_butterfly
->setPublicLength(oldLength
- count
);
820 case ArrayWithArrayStorage
:
821 case ArrayWithSlowPutArrayStorage
:
822 return shiftCountWithArrayStorage(startIndex
, count
, arrayStorage());
830 // Returns true if the unshift can be handled, false to fallback.
831 bool JSArray::unshiftCountWithArrayStorage(ExecState
* exec
, unsigned startIndex
, unsigned count
, ArrayStorage
* storage
)
833 unsigned length
= storage
->length();
835 RELEASE_ASSERT(startIndex
<= length
);
837 // If the array contains holes or is otherwise in an abnormal state,
838 // use the generic algorithm in ArrayPrototype.
839 if (length
!= storage
->m_numValuesInVector
|| storage
->inSparseMode() || shouldUseSlowPut(structure()->indexingType()))
842 bool moveFront
= !startIndex
|| startIndex
< length
/ 2;
844 unsigned vectorLength
= storage
->vectorLength();
846 if (moveFront
&& storage
->m_indexBias
>= count
) {
847 m_butterfly
= storage
->butterfly()->unshift(structure(), count
);
848 storage
= m_butterfly
->arrayStorage();
849 storage
->m_indexBias
-= count
;
850 storage
->setVectorLength(vectorLength
+ count
);
851 } else if (!moveFront
&& vectorLength
- length
>= count
)
852 storage
= storage
->butterfly()->arrayStorage();
853 else if (unshiftCountSlowCase(exec
->vm(), moveFront
, count
))
854 storage
= arrayStorage();
856 throwOutOfMemoryError(exec
);
860 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
864 memmove(vector
, vector
+ count
, startIndex
* sizeof(JSValue
));
865 else if (length
- startIndex
)
866 memmove(vector
+ startIndex
+ count
, vector
+ startIndex
, (length
- startIndex
) * sizeof(JSValue
));
869 for (unsigned i
= 0; i
< count
; i
++)
870 vector
[i
+ startIndex
].clear();
874 bool JSArray::unshiftCountWithAnyIndexingType(ExecState
* exec
, unsigned startIndex
, unsigned count
)
876 switch (structure()->indexingType()) {
878 case ArrayWithUndecided
:
879 // We could handle this. But it shouldn't ever come up, so we won't.
883 case ArrayWithContiguous
: {
884 unsigned oldLength
= m_butterfly
->publicLength();
886 // We may have to walk the entire array to do the unshift. We're willing to do so
887 // only if it's not horribly slow.
888 if (oldLength
- startIndex
>= MIN_SPARSE_ARRAY_INDEX
)
889 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
891 ensureLength(exec
->vm(), oldLength
+ count
);
893 // We have to check for holes before we start moving things around so that we don't get halfway
894 // through shifting and then realize we should have been in ArrayStorage mode.
895 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
896 JSValue v
= m_butterfly
->contiguous()[i
].get();
898 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
901 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
902 JSValue v
= m_butterfly
->contiguous()[i
].get();
904 m_butterfly
->contiguous()[i
+ count
].setWithoutWriteBarrier(v
);
907 // NOTE: we're leaving being garbage in the part of the array that we shifted out
908 // of. This is fine because the caller is required to store over that area, and
909 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
910 // as storing over an existing element.
915 case ArrayWithDouble
: {
916 unsigned oldLength
= m_butterfly
->publicLength();
918 // We may have to walk the entire array to do the unshift. We're willing to do so
919 // only if it's not horribly slow.
920 if (oldLength
- startIndex
>= MIN_SPARSE_ARRAY_INDEX
)
921 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
923 ensureLength(exec
->vm(), oldLength
+ count
);
925 // We have to check for holes before we start moving things around so that we don't get halfway
926 // through shifting and then realize we should have been in ArrayStorage mode.
927 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
928 double v
= m_butterfly
->contiguousDouble()[i
];
929 if (UNLIKELY(v
!= v
))
930 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, ensureArrayStorage(exec
->vm()));
933 for (unsigned i
= oldLength
; i
-- > startIndex
;) {
934 double v
= m_butterfly
->contiguousDouble()[i
];
936 m_butterfly
->contiguousDouble()[i
+ count
] = v
;
939 // NOTE: we're leaving being garbage in the part of the array that we shifted out
940 // of. This is fine because the caller is required to store over that area, and
941 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
942 // as storing over an existing element.
947 case ArrayWithArrayStorage
:
948 case ArrayWithSlowPutArrayStorage
:
949 return unshiftCountWithArrayStorage(exec
, startIndex
, count
, arrayStorage());
957 static int compareNumbersForQSortWithInt32(const void* a
, const void* b
)
959 int32_t ia
= static_cast<const JSValue
*>(a
)->asInt32();
960 int32_t ib
= static_cast<const JSValue
*>(b
)->asInt32();
964 static int compareNumbersForQSortWithDouble(const void* a
, const void* b
)
966 double da
= *static_cast<const double*>(a
);
967 double db
= *static_cast<const double*>(b
);
968 return (da
> db
) - (da
< db
);
971 static int compareNumbersForQSort(const void* a
, const void* b
)
973 double da
= static_cast<const JSValue
*>(a
)->asNumber();
974 double db
= static_cast<const JSValue
*>(b
)->asNumber();
975 return (da
> db
) - (da
< db
);
978 static int compareByStringPairForQSort(const void* a
, const void* b
)
980 const ValueStringPair
* va
= static_cast<const ValueStringPair
*>(a
);
981 const ValueStringPair
* vb
= static_cast<const ValueStringPair
*>(b
);
982 return codePointCompare(va
->second
, vb
->second
);
985 template<IndexingType indexingType
>
986 void JSArray::sortNumericVector(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
988 ASSERT(indexingType
== ArrayWithInt32
|| indexingType
== ArrayWithDouble
|| indexingType
== ArrayWithContiguous
|| indexingType
== ArrayWithArrayStorage
);
990 unsigned lengthNotIncludingUndefined
;
991 unsigned newRelevantLength
;
992 compactForSorting
<indexingType
>(
993 lengthNotIncludingUndefined
,
996 ContiguousJSValues data
= indexingData
<indexingType
>();
998 if (indexingType
== ArrayWithArrayStorage
&& arrayStorage()->m_sparseMap
.get()) {
999 throwOutOfMemoryError(exec
);
1003 if (!lengthNotIncludingUndefined
)
1006 bool allValuesAreNumbers
= true;
1007 switch (indexingType
) {
1008 case ArrayWithInt32
:
1009 case ArrayWithDouble
:
1013 for (size_t i
= 0; i
< newRelevantLength
; ++i
) {
1014 if (!data
[i
].isNumber()) {
1015 allValuesAreNumbers
= false;
1022 if (!allValuesAreNumbers
)
1023 return sort(exec
, compareFunction
, callType
, callData
);
1025 // For numeric comparison, which is fast, qsort is faster than mergesort. We
1026 // also don't require mergesort's stability, since there's no user visible
1027 // side-effect from swapping the order of equal primitive values.
1028 int (*compare
)(const void*, const void*);
1029 switch (indexingType
) {
1030 case ArrayWithInt32
:
1031 compare
= compareNumbersForQSortWithInt32
;
1034 case ArrayWithDouble
:
1035 compare
= compareNumbersForQSortWithDouble
;
1036 ASSERT(sizeof(WriteBarrier
<Unknown
>) == sizeof(double));
1040 compare
= compareNumbersForQSort
;
1043 ASSERT(data
.length() >= newRelevantLength
);
1044 qsort(data
.data(), newRelevantLength
, sizeof(WriteBarrier
<Unknown
>), compare
);
1048 void JSArray::sortNumeric(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1050 ASSERT(!inSparseIndexingMode());
1052 switch (structure()->indexingType()) {
1056 case ArrayWithInt32
:
1057 sortNumericVector
<ArrayWithInt32
>(exec
, compareFunction
, callType
, callData
);
1060 case ArrayWithDouble
:
1061 sortNumericVector
<ArrayWithDouble
>(exec
, compareFunction
, callType
, callData
);
1064 case ArrayWithContiguous
:
1065 sortNumericVector
<ArrayWithContiguous
>(exec
, compareFunction
, callType
, callData
);
1068 case ArrayWithArrayStorage
:
1069 sortNumericVector
<ArrayWithArrayStorage
>(exec
, compareFunction
, callType
, callData
);
1078 template <IndexingType
> struct ContiguousTypeAccessor
{
1079 typedef WriteBarrier
<Unknown
> Type
;
1080 static JSValue
getAsValue(ContiguousData
<Type
> data
, size_t i
) { return data
[i
].get(); }
1081 static void setWithValue(VM
& vm
, JSArray
* thisValue
, ContiguousData
<Type
> data
, size_t i
, JSValue value
)
1083 data
[i
].set(vm
, thisValue
, value
);
1085 static void replaceDataReference(ContiguousData
<Type
>* outData
, ContiguousJSValues inData
)
1091 template <> struct ContiguousTypeAccessor
<ArrayWithDouble
> {
1092 typedef double Type
;
1093 static JSValue
getAsValue(ContiguousData
<Type
> data
, size_t i
) { ASSERT(data
[i
] == data
[i
]); return JSValue(JSValue::EncodeAsDouble
, data
[i
]); }
1094 static void setWithValue(VM
&, JSArray
*, ContiguousData
<Type
> data
, size_t i
, JSValue value
)
1096 data
[i
] = value
.asNumber();
1098 static NO_RETURN_DUE_TO_CRASH
void replaceDataReference(ContiguousData
<Type
>*, ContiguousJSValues
)
1100 RELEASE_ASSERT_WITH_MESSAGE(0, "Inconsistent indexing types during compact array sort.");
1105 template<IndexingType indexingType
, typename StorageType
>
1106 void JSArray::sortCompactedVector(ExecState
* exec
, ContiguousData
<StorageType
> data
, unsigned relevantLength
)
1108 if (!relevantLength
)
1111 VM
& vm
= exec
->vm();
1113 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
1114 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
1115 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
1116 // random or otherwise changing results, effectively making compare function inconsistent.
1118 Vector
<ValueStringPair
, 0, UnsafeVectorOverflow
> values(relevantLength
);
1119 if (!values
.begin()) {
1120 throwOutOfMemoryError(exec
);
1124 Heap::heap(this)->pushTempSortVector(&values
);
1126 bool isSortingPrimitiveValues
= true;
1128 for (size_t i
= 0; i
< relevantLength
; i
++) {
1129 JSValue value
= ContiguousTypeAccessor
<indexingType
>::getAsValue(data
, i
);
1130 ASSERT(indexingType
!= ArrayWithInt32
|| value
.isInt32());
1131 ASSERT(!value
.isUndefined());
1132 values
[i
].first
= value
;
1133 if (indexingType
!= ArrayWithDouble
&& indexingType
!= ArrayWithInt32
)
1134 isSortingPrimitiveValues
= isSortingPrimitiveValues
&& value
.isPrimitive();
1137 // FIXME: The following loop continues to call toString on subsequent values even after
1138 // a toString call raises an exception.
1140 for (size_t i
= 0; i
< relevantLength
; i
++)
1141 values
[i
].second
= values
[i
].first
.toWTFStringInline(exec
);
1143 if (exec
->hadException()) {
1144 Heap::heap(this)->popTempSortVector(&values
);
1148 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
1152 if (isSortingPrimitiveValues
)
1153 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1155 mergesort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1157 // FIXME: The qsort library function is likely to not be a stable sort.
1158 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
1159 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
1162 // If the toString function changed the length of the array or vector storage,
1163 // increase the length to handle the orignal number of actual values.
1164 switch (indexingType
) {
1165 case ArrayWithInt32
:
1166 case ArrayWithDouble
:
1167 case ArrayWithContiguous
:
1168 ensureLength(vm
, relevantLength
);
1171 case ArrayWithArrayStorage
:
1172 if (arrayStorage()->vectorLength() < relevantLength
) {
1173 increaseVectorLength(exec
->vm(), relevantLength
);
1174 ContiguousTypeAccessor
<indexingType
>::replaceDataReference(&data
, arrayStorage()->vector());
1176 if (arrayStorage()->length() < relevantLength
)
1177 arrayStorage()->setLength(relevantLength
);
1184 for (size_t i
= 0; i
< relevantLength
; i
++)
1185 ContiguousTypeAccessor
<indexingType
>::setWithValue(vm
, this, data
, i
, values
[i
].first
);
1187 Heap::heap(this)->popTempSortVector(&values
);
1190 void JSArray::sort(ExecState
* exec
)
1192 ASSERT(!inSparseIndexingMode());
1194 switch (structure()->indexingType()) {
1196 case ArrayWithUndecided
:
1199 case ArrayWithInt32
: {
1200 unsigned lengthNotIncludingUndefined
;
1201 unsigned newRelevantLength
;
1202 compactForSorting
<ArrayWithInt32
>(
1203 lengthNotIncludingUndefined
, newRelevantLength
);
1205 sortCompactedVector
<ArrayWithInt32
>(
1206 exec
, m_butterfly
->contiguousInt32(), lengthNotIncludingUndefined
);
1210 case ArrayWithDouble
: {
1211 unsigned lengthNotIncludingUndefined
;
1212 unsigned newRelevantLength
;
1213 compactForSorting
<ArrayWithDouble
>(
1214 lengthNotIncludingUndefined
, newRelevantLength
);
1216 sortCompactedVector
<ArrayWithDouble
>(
1217 exec
, m_butterfly
->contiguousDouble(), lengthNotIncludingUndefined
);
1221 case ArrayWithContiguous
: {
1222 unsigned lengthNotIncludingUndefined
;
1223 unsigned newRelevantLength
;
1224 compactForSorting
<ArrayWithContiguous
>(
1225 lengthNotIncludingUndefined
, newRelevantLength
);
1227 sortCompactedVector
<ArrayWithContiguous
>(
1228 exec
, m_butterfly
->contiguous(), lengthNotIncludingUndefined
);
1232 case ArrayWithArrayStorage
: {
1233 unsigned lengthNotIncludingUndefined
;
1234 unsigned newRelevantLength
;
1235 compactForSorting
<ArrayWithArrayStorage
>(
1236 lengthNotIncludingUndefined
, newRelevantLength
);
1237 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
1238 ASSERT(!storage
->m_sparseMap
);
1240 sortCompactedVector
<ArrayWithArrayStorage
>(exec
, storage
->vector(), lengthNotIncludingUndefined
);
1245 RELEASE_ASSERT_NOT_REACHED();
1249 struct AVLTreeNodeForArrayCompare
{
1252 // Child pointers. The high bit of gt is robbed and used as the
1253 // balance factor sign. The high bit of lt is robbed and used as
1254 // the magnitude of the balance factor.
1259 struct AVLTreeAbstractorForArrayCompare
{
1260 typedef int32_t handle
; // Handle is an index into m_nodes vector.
1261 typedef JSValue key
;
1262 typedef int32_t size
;
1264 Vector
<AVLTreeNodeForArrayCompare
, 0, UnsafeVectorOverflow
> m_nodes
;
1266 JSValue m_compareFunction
;
1267 CallType m_compareCallType
;
1268 const CallData
* m_compareCallData
;
1269 OwnPtr
<CachedCall
> m_cachedCall
;
1271 handle
get_less(handle h
) { return m_nodes
[h
].lt
& 0x7FFFFFFF; }
1272 void set_less(handle h
, handle lh
) { m_nodes
[h
].lt
&= 0x80000000; m_nodes
[h
].lt
|= lh
; }
1273 handle
get_greater(handle h
) { return m_nodes
[h
].gt
& 0x7FFFFFFF; }
1274 void set_greater(handle h
, handle gh
) { m_nodes
[h
].gt
&= 0x80000000; m_nodes
[h
].gt
|= gh
; }
1276 int get_balance_factor(handle h
)
1278 if (m_nodes
[h
].gt
& 0x80000000)
1280 return static_cast<unsigned>(m_nodes
[h
].lt
) >> 31;
1283 void set_balance_factor(handle h
, int bf
)
1286 m_nodes
[h
].lt
&= 0x7FFFFFFF;
1287 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1289 m_nodes
[h
].lt
|= 0x80000000;
1291 m_nodes
[h
].gt
|= 0x80000000;
1293 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1297 int compare_key_key(key va
, key vb
)
1299 ASSERT(!va
.isUndefined());
1300 ASSERT(!vb
.isUndefined());
1302 if (m_exec
->hadException())
1305 double compareResult
;
1307 m_cachedCall
->setThis(jsUndefined());
1308 m_cachedCall
->setArgument(0, va
);
1309 m_cachedCall
->setArgument(1, vb
);
1310 compareResult
= m_cachedCall
->call().toNumber(m_cachedCall
->newCallFrame(m_exec
));
1312 MarkedArgumentBuffer arguments
;
1313 arguments
.append(va
);
1314 arguments
.append(vb
);
1315 compareResult
= call(m_exec
, m_compareFunction
, m_compareCallType
, *m_compareCallData
, jsUndefined(), arguments
).toNumber(m_exec
);
1317 return (compareResult
< 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1320 int compare_key_node(key k
, handle h
) { return compare_key_key(k
, m_nodes
[h
].value
); }
1321 int compare_node_node(handle h1
, handle h2
) { return compare_key_key(m_nodes
[h1
].value
, m_nodes
[h2
].value
); }
1323 static handle
null() { return 0x7FFFFFFF; }
1326 template<IndexingType indexingType
>
1327 void JSArray::sortVector(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1329 ASSERT(!inSparseIndexingMode());
1330 ASSERT(indexingType
== structure()->indexingType());
1332 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1334 // The maximum tree depth is compiled in - but the caller is clearly up to no good
1335 // if a larger array is passed.
1336 ASSERT(m_butterfly
->publicLength() <= static_cast<unsigned>(std::numeric_limits
<int>::max()));
1337 if (m_butterfly
->publicLength() > static_cast<unsigned>(std::numeric_limits
<int>::max()))
1340 unsigned usedVectorLength
= relevantLength
<indexingType
>();
1341 unsigned nodeCount
= usedVectorLength
;
1346 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44> tree
; // Depth 44 is enough for 2^31 items
1347 tree
.abstractor().m_exec
= exec
;
1348 tree
.abstractor().m_compareFunction
= compareFunction
;
1349 tree
.abstractor().m_compareCallType
= callType
;
1350 tree
.abstractor().m_compareCallData
= &callData
;
1351 tree
.abstractor().m_nodes
.grow(nodeCount
);
1353 if (callType
== CallTypeJS
)
1354 tree
.abstractor().m_cachedCall
= adoptPtr(new CachedCall(exec
, jsCast
<JSFunction
*>(compareFunction
), 2));
1356 if (!tree
.abstractor().m_nodes
.begin()) {
1357 throwOutOfMemoryError(exec
);
1361 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1362 // right out from under us while we're building the tree here.
1364 unsigned numDefined
= 0;
1365 unsigned numUndefined
= 0;
1367 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1368 for (; numDefined
< usedVectorLength
; ++numDefined
) {
1369 if (numDefined
>= m_butterfly
->vectorLength())
1371 JSValue v
= getHolyIndexQuickly(numDefined
);
1372 if (!v
|| v
.isUndefined())
1374 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1375 tree
.insert(numDefined
);
1377 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
1378 if (i
>= m_butterfly
->vectorLength())
1380 JSValue v
= getHolyIndexQuickly(i
);
1382 if (v
.isUndefined())
1385 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1386 tree
.insert(numDefined
);
1392 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1394 // The array size may have changed. Figure out the new bounds.
1395 unsigned newestUsedVectorLength
= currentRelevantLength();
1397 unsigned elementsToExtractThreshold
= min(min(newestUsedVectorLength
, numDefined
), static_cast<unsigned>(tree
.abstractor().m_nodes
.size()));
1398 unsigned undefinedElementsThreshold
= min(newestUsedVectorLength
, newUsedVectorLength
);
1399 unsigned clearElementsThreshold
= min(newestUsedVectorLength
, usedVectorLength
);
1401 // Copy the values back into m_storage.
1402 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44>::Iterator iter
;
1403 iter
.start_iter_least(tree
);
1404 VM
& vm
= exec
->vm();
1405 for (unsigned i
= 0; i
< elementsToExtractThreshold
; ++i
) {
1406 ASSERT(i
< butterfly()->vectorLength());
1407 if (structure()->indexingType() == ArrayWithDouble
)
1408 butterfly()->contiguousDouble()[i
] = tree
.abstractor().m_nodes
[*iter
].value
.asNumber();
1410 currentIndexingData()[i
].set(vm
, this, tree
.abstractor().m_nodes
[*iter
].value
);
1413 // Put undefined values back in.
1414 switch (structure()->indexingType()) {
1415 case ArrayWithInt32
:
1416 case ArrayWithDouble
:
1417 ASSERT(elementsToExtractThreshold
== undefinedElementsThreshold
);
1421 for (unsigned i
= elementsToExtractThreshold
; i
< undefinedElementsThreshold
; ++i
) {
1422 ASSERT(i
< butterfly()->vectorLength());
1423 currentIndexingData()[i
].setUndefined();
1427 // Ensure that unused values in the vector are zeroed out.
1428 for (unsigned i
= undefinedElementsThreshold
; i
< clearElementsThreshold
; ++i
) {
1429 ASSERT(i
< butterfly()->vectorLength());
1430 if (structure()->indexingType() == ArrayWithDouble
)
1431 butterfly()->contiguousDouble()[i
] = QNaN
;
1433 currentIndexingData()[i
].clear();
1436 if (hasArrayStorage(structure()->indexingType()))
1437 arrayStorage()->m_numValuesInVector
= newUsedVectorLength
;
1440 void JSArray::sort(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1442 ASSERT(!inSparseIndexingMode());
1444 switch (structure()->indexingType()) {
1446 case ArrayWithUndecided
:
1449 case ArrayWithInt32
:
1450 sortVector
<ArrayWithInt32
>(exec
, compareFunction
, callType
, callData
);
1453 case ArrayWithDouble
:
1454 sortVector
<ArrayWithDouble
>(exec
, compareFunction
, callType
, callData
);
1457 case ArrayWithContiguous
:
1458 sortVector
<ArrayWithContiguous
>(exec
, compareFunction
, callType
, callData
);
1461 case ArrayWithArrayStorage
:
1462 sortVector
<ArrayWithArrayStorage
>(exec
, compareFunction
, callType
, callData
);
1466 RELEASE_ASSERT_NOT_REACHED();
1470 void JSArray::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
1474 WriteBarrier
<Unknown
>* vector
;
1476 switch (structure()->indexingType()) {
1480 case ArrayWithUndecided
: {
1486 case ArrayWithInt32
:
1487 case ArrayWithContiguous
: {
1488 vectorEnd
= m_butterfly
->publicLength();
1489 vector
= m_butterfly
->contiguous().data();
1493 case ArrayWithDouble
: {
1496 for (; i
< m_butterfly
->publicLength(); ++i
) {
1497 double v
= butterfly()->contiguousDouble()[i
];
1500 args
.append(JSValue(JSValue::EncodeAsDouble
, v
));
1505 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
: {
1506 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
1508 vector
= storage
->m_vector
;
1509 vectorEnd
= min(storage
->length(), storage
->vectorLength());
1520 for (; i
< vectorEnd
; ++i
) {
1521 WriteBarrier
<Unknown
>& v
= vector
[i
];
1524 args
.append(v
.get());
1527 for (; i
< length(); ++i
)
1528 args
.append(get(exec
, i
));
1531 void JSArray::copyToArguments(ExecState
* exec
, CallFrame
* callFrame
, uint32_t length
)
1534 WriteBarrier
<Unknown
>* vector
;
1537 ASSERT(length
== this->length());
1538 switch (structure()->indexingType()) {
1542 case ArrayWithUndecided
: {
1548 case ArrayWithInt32
:
1549 case ArrayWithContiguous
: {
1550 vector
= m_butterfly
->contiguous().data();
1551 vectorEnd
= m_butterfly
->publicLength();
1555 case ArrayWithDouble
: {
1558 for (; i
< m_butterfly
->publicLength(); ++i
) {
1559 ASSERT(i
< butterfly()->vectorLength());
1560 double v
= m_butterfly
->contiguousDouble()[i
];
1563 callFrame
->setArgument(i
, JSValue(JSValue::EncodeAsDouble
, v
));
1568 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
: {
1569 ArrayStorage
* storage
= m_butterfly
->arrayStorage();
1570 vector
= storage
->m_vector
;
1571 vectorEnd
= min(length
, storage
->vectorLength());
1582 for (; i
< vectorEnd
; ++i
) {
1583 WriteBarrier
<Unknown
>& v
= vector
[i
];
1586 callFrame
->setArgument(i
, v
.get());
1589 for (; i
< length
; ++i
)
1590 callFrame
->setArgument(i
, get(exec
, i
));
1593 template<IndexingType indexingType
>
1594 void JSArray::compactForSorting(unsigned& numDefined
, unsigned& newRelevantLength
)
1596 ASSERT(!inSparseIndexingMode());
1597 ASSERT(indexingType
== structure()->indexingType());
1599 unsigned myRelevantLength
= relevantLength
<indexingType
>();
1602 unsigned numUndefined
= 0;
1604 for (; numDefined
< myRelevantLength
; ++numDefined
) {
1605 ASSERT(numDefined
< m_butterfly
->vectorLength());
1606 if (indexingType
== ArrayWithInt32
) {
1607 JSValue v
= m_butterfly
->contiguousInt32()[numDefined
].get();
1610 ASSERT(v
.isInt32());
1613 if (indexingType
== ArrayWithDouble
) {
1614 double v
= m_butterfly
->contiguousDouble()[numDefined
];
1619 JSValue v
= indexingData
<indexingType
>()[numDefined
].get();
1620 if (!v
|| v
.isUndefined())
1624 for (unsigned i
= numDefined
; i
< myRelevantLength
; ++i
) {
1625 ASSERT(i
< m_butterfly
->vectorLength());
1626 if (indexingType
== ArrayWithInt32
) {
1627 JSValue v
= m_butterfly
->contiguousInt32()[i
].get();
1630 ASSERT(v
.isInt32());
1631 ASSERT(numDefined
< m_butterfly
->vectorLength());
1632 m_butterfly
->contiguousInt32()[numDefined
++].setWithoutWriteBarrier(v
);
1635 if (indexingType
== ArrayWithDouble
) {
1636 double v
= m_butterfly
->contiguousDouble()[i
];
1639 ASSERT(numDefined
< m_butterfly
->vectorLength());
1640 m_butterfly
->contiguousDouble()[numDefined
++] = v
;
1643 JSValue v
= indexingData
<indexingType
>()[i
].get();
1645 if (v
.isUndefined())
1648 ASSERT(numDefined
< m_butterfly
->vectorLength());
1649 indexingData
<indexingType
>()[numDefined
++].setWithoutWriteBarrier(v
);
1654 newRelevantLength
= numDefined
+ numUndefined
;
1656 if (hasArrayStorage(indexingType
))
1657 RELEASE_ASSERT(!arrayStorage()->m_sparseMap
);
1659 switch (indexingType
) {
1660 case ArrayWithInt32
:
1661 case ArrayWithDouble
:
1662 RELEASE_ASSERT(numDefined
== newRelevantLength
);
1666 for (unsigned i
= numDefined
; i
< newRelevantLength
; ++i
) {
1667 ASSERT(i
< m_butterfly
->vectorLength());
1668 indexingData
<indexingType
>()[i
].setUndefined();
1672 for (unsigned i
= newRelevantLength
; i
< myRelevantLength
; ++i
) {
1673 ASSERT(i
< m_butterfly
->vectorLength());
1674 if (indexingType
== ArrayWithDouble
)
1675 m_butterfly
->contiguousDouble()[i
] = QNaN
;
1677 indexingData
<indexingType
>()[i
].clear();
1680 if (hasArrayStorage(indexingType
))
1681 arrayStorage()->m_numValuesInVector
= newRelevantLength
;