2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008 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 "PropertyNameArray.h"
28 #include <wtf/AVLTree.h>
29 #include <wtf/Assertions.h>
30 #include <Operations.h>
32 #define CHECK_ARRAY_CONSISTENCY 0
39 ASSERT_CLASS_FITS_IN_CELL(JSArray
);
41 // Overview of JSArray
43 // Properties of JSArray objects may be stored in one of three locations:
44 // * The regular JSObject property map.
45 // * A storage vector.
46 // * A sparse map of array entries.
48 // Properties with non-numeric identifiers, with identifiers that are not representable
49 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX
50 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
51 // integer) are not considered array indices and will be stored in the JSObject property map.
53 // All properties with a numeric identifer, representable as an unsigned integer i,
54 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
55 // storage vector or the sparse map. An array index i will be handled in the following
58 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector.
59 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
60 // be stored in the storage vector or in the sparse array, depending on the density of
61 // data that would be stored in the vector (a vector being used where at least
62 // (1 / minDensityMultiplier) of the entries would be populated).
63 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
64 // in the sparse array.
66 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
67 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
68 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValuePtr)) +
69 // (vectorLength * sizeof(JSValuePtr)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
70 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValuePtr))) / sizeof(JSValuePtr))
72 // These values have to be macros to be used in max() and min() without introducing
73 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
74 #define MIN_SPARSE_ARRAY_INDEX 10000U
75 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
76 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
77 #define MAX_ARRAY_INDEX 0xFFFFFFFEU
79 // Our policy for when to use a vector and when to use a sparse map.
80 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
81 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
82 // as long as it is 1/8 full. If more sparse than that, we use a map.
83 static const unsigned minDensityMultiplier
= 8;
85 const ClassInfo
JSArray::info
= {"Array", 0, 0, 0};
87 static inline size_t storageSize(unsigned vectorLength
)
89 ASSERT(vectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
91 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
92 // - as asserted above - the following calculation cannot overflow.
93 size_t size
= (sizeof(ArrayStorage
) - sizeof(JSValuePtr
)) + (vectorLength
* sizeof(JSValuePtr
));
94 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
95 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
96 ASSERT(((size
- (sizeof(ArrayStorage
) - sizeof(JSValuePtr
))) / sizeof(JSValuePtr
) == vectorLength
) && (size
>= (sizeof(ArrayStorage
) - sizeof(JSValuePtr
))));
101 static inline unsigned increasedVectorLength(unsigned newLength
)
103 ASSERT(newLength
<= MAX_STORAGE_VECTOR_LENGTH
);
105 // Mathematically equivalent to:
106 // increasedLength = (newLength * 3 + 1) / 2;
108 // increasedLength = (unsigned)ceil(newLength * 1.5));
109 // This form is not prone to internal overflow.
110 unsigned increasedLength
= newLength
+ (newLength
>> 1) + (newLength
& 1);
111 ASSERT(increasedLength
>= newLength
);
113 return min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
116 static inline bool isDenseEnoughForVector(unsigned length
, unsigned numValues
)
118 return length
/ minDensityMultiplier
<= numValues
;
121 #if !CHECK_ARRAY_CONSISTENCY
123 inline void JSArray::checkConsistency(ConsistencyCheckType
)
129 JSArray::JSArray(PassRefPtr
<Structure
> structure
)
130 : JSObject(structure
)
132 unsigned initialCapacity
= 0;
134 m_storage
= static_cast<ArrayStorage
*>(fastZeroedMalloc(storageSize(initialCapacity
)));
135 m_fastAccessCutoff
= 0;
136 m_storage
->m_vectorLength
= initialCapacity
;
137 m_storage
->m_length
= 0;
142 JSArray::JSArray(PassRefPtr
<Structure
> structure
, unsigned initialLength
)
143 : JSObject(structure
)
145 unsigned initialCapacity
= min(initialLength
, MIN_SPARSE_ARRAY_INDEX
);
147 m_storage
= static_cast<ArrayStorage
*>(fastZeroedMalloc(storageSize(initialCapacity
)));
148 m_fastAccessCutoff
= 0;
149 m_storage
->m_vectorLength
= initialCapacity
;
150 m_storage
->m_length
= initialLength
;
152 Heap::heap(this)->reportExtraMemoryCost(initialCapacity
* sizeof(JSValuePtr
));
157 JSArray::JSArray(ExecState
* exec
, PassRefPtr
<Structure
> structure
, const ArgList
& list
)
158 : JSObject(structure
)
160 unsigned length
= list
.size();
162 m_fastAccessCutoff
= length
;
164 ArrayStorage
* storage
= static_cast<ArrayStorage
*>(fastMalloc(storageSize(length
)));
166 storage
->m_vectorLength
= length
;
167 storage
->m_numValuesInVector
= length
;
168 storage
->m_sparseValueMap
= 0;
169 storage
->m_length
= length
;
172 ArgList::const_iterator end
= list
.end();
173 for (ArgList::const_iterator it
= list
.begin(); it
!= end
; ++it
, ++i
)
174 storage
->m_vector
[i
] = (*it
).jsValue(exec
);
178 Heap::heap(this)->reportExtraMemoryCost(storageSize(length
));
185 checkConsistency(DestructorConsistencyCheck
);
187 delete m_storage
->m_sparseValueMap
;
191 bool JSArray::getOwnPropertySlot(ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
193 ArrayStorage
* storage
= m_storage
;
195 if (i
>= storage
->m_length
) {
196 if (i
> MAX_ARRAY_INDEX
)
197 return getOwnPropertySlot(exec
, Identifier::from(exec
, i
), slot
);
201 if (i
< storage
->m_vectorLength
) {
202 JSValuePtr
& valueSlot
= storage
->m_vector
[i
];
204 slot
.setValueSlot(&valueSlot
);
207 } else if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
208 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
209 SparseArrayValueMap::iterator it
= map
->find(i
);
210 if (it
!= map
->end()) {
211 slot
.setValueSlot(&it
->second
);
220 bool JSArray::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
222 if (propertyName
== exec
->propertyNames().length
) {
223 slot
.setValue(jsNumber(exec
, length()));
228 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
230 return JSArray::getOwnPropertySlot(exec
, i
, slot
);
232 return JSObject::getOwnPropertySlot(exec
, propertyName
, slot
);
236 void JSArray::put(ExecState
* exec
, const Identifier
& propertyName
, JSValuePtr value
, PutPropertySlot
& slot
)
239 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
245 if (propertyName
== exec
->propertyNames().length
) {
246 unsigned newLength
= value
.toUInt32(exec
);
247 if (value
.toNumber(exec
) != static_cast<double>(newLength
)) {
248 throwError(exec
, RangeError
, "Invalid array length.");
251 setLength(newLength
);
255 JSObject::put(exec
, propertyName
, value
, slot
);
258 void JSArray::put(ExecState
* exec
, unsigned i
, JSValuePtr value
)
262 unsigned length
= m_storage
->m_length
;
263 if (i
>= length
&& i
<= MAX_ARRAY_INDEX
) {
265 m_storage
->m_length
= length
;
268 if (i
< m_storage
->m_vectorLength
) {
269 JSValuePtr
& valueSlot
= m_storage
->m_vector
[i
];
276 if (++m_storage
->m_numValuesInVector
== m_storage
->m_length
)
277 m_fastAccessCutoff
= m_storage
->m_length
;
282 putSlowCase(exec
, i
, value
);
285 NEVER_INLINE
void JSArray::putSlowCase(ExecState
* exec
, unsigned i
, JSValuePtr value
)
287 ArrayStorage
* storage
= m_storage
;
288 SparseArrayValueMap
* map
= storage
->m_sparseValueMap
;
290 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
291 if (i
> MAX_ARRAY_INDEX
) {
292 PutPropertySlot slot
;
293 put(exec
, Identifier::from(exec
, i
), value
, slot
);
297 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
298 // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
299 if ((i
> MAX_STORAGE_VECTOR_INDEX
) || !isDenseEnoughForVector(i
+ 1, storage
->m_numValuesInVector
+ 1)) {
301 map
= new SparseArrayValueMap
;
302 storage
->m_sparseValueMap
= map
;
309 // We have decided that we'll put the new item into the vector.
310 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
311 if (!map
|| map
->isEmpty()) {
312 if (increaseVectorLength(i
+ 1)) {
314 storage
->m_vector
[i
] = value
;
315 if (++storage
->m_numValuesInVector
== storage
->m_length
)
316 m_fastAccessCutoff
= storage
->m_length
;
319 throwOutOfMemoryError(exec
);
323 // Decide how many values it would be best to move from the map.
324 unsigned newNumValuesInVector
= storage
->m_numValuesInVector
+ 1;
325 unsigned newVectorLength
= increasedVectorLength(i
+ 1);
326 for (unsigned j
= max(storage
->m_vectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< newVectorLength
; ++j
)
327 newNumValuesInVector
+= map
->contains(j
);
328 if (i
>= MIN_SPARSE_ARRAY_INDEX
)
329 newNumValuesInVector
-= map
->contains(i
);
330 if (isDenseEnoughForVector(newVectorLength
, newNumValuesInVector
)) {
331 unsigned proposedNewNumValuesInVector
= newNumValuesInVector
;
332 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
333 while (newVectorLength
< MAX_STORAGE_VECTOR_LENGTH
) {
334 unsigned proposedNewVectorLength
= increasedVectorLength(newVectorLength
+ 1);
335 for (unsigned j
= max(newVectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< proposedNewVectorLength
; ++j
)
336 proposedNewNumValuesInVector
+= map
->contains(j
);
337 if (!isDenseEnoughForVector(proposedNewVectorLength
, proposedNewNumValuesInVector
))
339 newVectorLength
= proposedNewVectorLength
;
340 newNumValuesInVector
= proposedNewNumValuesInVector
;
344 storage
= static_cast<ArrayStorage
*>(tryFastRealloc(storage
, storageSize(newVectorLength
)));
346 throwOutOfMemoryError(exec
);
350 unsigned vectorLength
= storage
->m_vectorLength
;
352 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
354 if (newNumValuesInVector
== storage
->m_numValuesInVector
+ 1) {
355 for (unsigned j
= vectorLength
; j
< newVectorLength
; ++j
)
356 storage
->m_vector
[j
] = noValue();
357 if (i
> MIN_SPARSE_ARRAY_INDEX
)
360 for (unsigned j
= vectorLength
; j
< max(vectorLength
, MIN_SPARSE_ARRAY_INDEX
); ++j
)
361 storage
->m_vector
[j
] = noValue();
362 for (unsigned j
= max(vectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< newVectorLength
; ++j
)
363 storage
->m_vector
[j
] = map
->take(j
);
366 storage
->m_vector
[i
] = value
;
368 storage
->m_vectorLength
= newVectorLength
;
369 storage
->m_numValuesInVector
= newNumValuesInVector
;
376 bool JSArray::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
379 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
381 return deleteProperty(exec
, i
);
383 if (propertyName
== exec
->propertyNames().length
)
386 return JSObject::deleteProperty(exec
, propertyName
);
389 bool JSArray::deleteProperty(ExecState
* exec
, unsigned i
)
393 ArrayStorage
* storage
= m_storage
;
395 if (i
< storage
->m_vectorLength
) {
396 JSValuePtr
& valueSlot
= storage
->m_vector
[i
];
401 valueSlot
= noValue();
402 --storage
->m_numValuesInVector
;
403 if (m_fastAccessCutoff
> i
)
404 m_fastAccessCutoff
= i
;
409 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
410 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
411 SparseArrayValueMap::iterator it
= map
->find(i
);
412 if (it
!= map
->end()) {
422 if (i
> MAX_ARRAY_INDEX
)
423 return deleteProperty(exec
, Identifier::from(exec
, i
));
428 void JSArray::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
430 // FIXME: Filling PropertyNameArray with an identifier for every integer
431 // is incredibly inefficient for large arrays. We need a different approach,
432 // which almost certainly means a different structure for PropertyNameArray.
434 ArrayStorage
* storage
= m_storage
;
436 unsigned usedVectorLength
= min(storage
->m_length
, storage
->m_vectorLength
);
437 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
438 if (storage
->m_vector
[i
])
439 propertyNames
.add(Identifier::from(exec
, i
));
442 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
443 SparseArrayValueMap::iterator end
= map
->end();
444 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
)
445 propertyNames
.add(Identifier::from(exec
, it
->first
));
448 JSObject::getPropertyNames(exec
, propertyNames
);
451 bool JSArray::increaseVectorLength(unsigned newLength
)
453 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
454 // to the vector. Callers have to account for that, because they can do it more efficiently.
456 ArrayStorage
* storage
= m_storage
;
458 unsigned vectorLength
= storage
->m_vectorLength
;
459 ASSERT(newLength
> vectorLength
);
460 ASSERT(newLength
<= MAX_STORAGE_VECTOR_INDEX
);
461 unsigned newVectorLength
= increasedVectorLength(newLength
);
463 storage
= static_cast<ArrayStorage
*>(tryFastRealloc(storage
, storageSize(newVectorLength
)));
467 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
468 storage
->m_vectorLength
= newVectorLength
;
470 for (unsigned i
= vectorLength
; i
< newVectorLength
; ++i
)
471 storage
->m_vector
[i
] = noValue();
477 void JSArray::setLength(unsigned newLength
)
481 ArrayStorage
* storage
= m_storage
;
483 unsigned length
= m_storage
->m_length
;
485 if (newLength
< length
) {
486 if (m_fastAccessCutoff
> newLength
)
487 m_fastAccessCutoff
= newLength
;
489 unsigned usedVectorLength
= min(length
, storage
->m_vectorLength
);
490 for (unsigned i
= newLength
; i
< usedVectorLength
; ++i
) {
491 JSValuePtr
& valueSlot
= storage
->m_vector
[i
];
492 bool hadValue
= valueSlot
;
493 valueSlot
= noValue();
494 storage
->m_numValuesInVector
-= hadValue
;
497 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
498 SparseArrayValueMap copy
= *map
;
499 SparseArrayValueMap::iterator end
= copy
.end();
500 for (SparseArrayValueMap::iterator it
= copy
.begin(); it
!= end
; ++it
) {
501 if (it
->first
>= newLength
)
502 map
->remove(it
->first
);
504 if (map
->isEmpty()) {
506 storage
->m_sparseValueMap
= 0;
511 m_storage
->m_length
= newLength
;
516 JSValuePtr
JSArray::pop()
520 unsigned length
= m_storage
->m_length
;
522 return jsUndefined();
528 if (m_fastAccessCutoff
> length
) {
529 JSValuePtr
& valueSlot
= m_storage
->m_vector
[length
];
532 valueSlot
= noValue();
533 --m_storage
->m_numValuesInVector
;
534 m_fastAccessCutoff
= length
;
535 } else if (length
< m_storage
->m_vectorLength
) {
536 JSValuePtr
& valueSlot
= m_storage
->m_vector
[length
];
538 valueSlot
= noValue();
540 --m_storage
->m_numValuesInVector
;
542 result
= jsUndefined();
544 result
= jsUndefined();
545 if (SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
) {
546 SparseArrayValueMap::iterator it
= map
->find(length
);
547 if (it
!= map
->end()) {
550 if (map
->isEmpty()) {
552 m_storage
->m_sparseValueMap
= 0;
558 m_storage
->m_length
= length
;
565 void JSArray::push(ExecState
* exec
, JSValuePtr value
)
569 if (m_storage
->m_length
< m_storage
->m_vectorLength
) {
570 ASSERT(!m_storage
->m_vector
[m_storage
->m_length
]);
571 m_storage
->m_vector
[m_storage
->m_length
] = value
;
572 if (++m_storage
->m_numValuesInVector
== ++m_storage
->m_length
)
573 m_fastAccessCutoff
= m_storage
->m_length
;
578 if (m_storage
->m_length
< MIN_SPARSE_ARRAY_INDEX
) {
579 SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
;
580 if (!map
|| map
->isEmpty()) {
581 if (increaseVectorLength(m_storage
->m_length
+ 1)) {
582 m_storage
->m_vector
[m_storage
->m_length
] = value
;
583 if (++m_storage
->m_numValuesInVector
== ++m_storage
->m_length
)
584 m_fastAccessCutoff
= m_storage
->m_length
;
589 throwOutOfMemoryError(exec
);
594 putSlowCase(exec
, m_storage
->m_length
++, value
);
601 ArrayStorage
* storage
= m_storage
;
603 unsigned usedVectorLength
= min(storage
->m_length
, storage
->m_vectorLength
);
604 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
605 JSValuePtr value
= storage
->m_vector
[i
];
606 if (value
&& !value
.marked())
610 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
611 SparseArrayValueMap::iterator end
= map
->end();
612 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
) {
613 JSValuePtr value
= it
->second
;
620 static int compareNumbersForQSort(const void* a
, const void* b
)
622 double da
= static_cast<const JSValuePtr
*>(a
)->uncheckedGetNumber();
623 double db
= static_cast<const JSValuePtr
*>(b
)->uncheckedGetNumber();
624 return (da
> db
) - (da
< db
);
627 typedef std::pair
<JSValuePtr
, UString
> ValueStringPair
;
629 static int compareByStringPairForQSort(const void* a
, const void* b
)
631 const ValueStringPair
* va
= static_cast<const ValueStringPair
*>(a
);
632 const ValueStringPair
* vb
= static_cast<const ValueStringPair
*>(b
);
633 return compare(va
->second
, vb
->second
);
636 void JSArray::sortNumeric(ExecState
* exec
, JSValuePtr compareFunction
, CallType callType
, const CallData
& callData
)
638 unsigned lengthNotIncludingUndefined
= compactForSorting();
639 if (m_storage
->m_sparseValueMap
) {
640 throwOutOfMemoryError(exec
);
644 if (!lengthNotIncludingUndefined
)
647 bool allValuesAreNumbers
= true;
648 size_t size
= m_storage
->m_numValuesInVector
;
649 for (size_t i
= 0; i
< size
; ++i
) {
650 if (!m_storage
->m_vector
[i
].isNumber()) {
651 allValuesAreNumbers
= false;
656 if (!allValuesAreNumbers
)
657 return sort(exec
, compareFunction
, callType
, callData
);
659 // For numeric comparison, which is fast, qsort is faster than mergesort. We
660 // also don't require mergesort's stability, since there's no user visible
661 // side-effect from swapping the order of equal primitive values.
662 qsort(m_storage
->m_vector
, size
, sizeof(JSValuePtr
), compareNumbersForQSort
);
664 checkConsistency(SortConsistencyCheck
);
667 void JSArray::sort(ExecState
* exec
)
669 unsigned lengthNotIncludingUndefined
= compactForSorting();
670 if (m_storage
->m_sparseValueMap
) {
671 throwOutOfMemoryError(exec
);
675 if (!lengthNotIncludingUndefined
)
678 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
679 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
680 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
681 // random or otherwise changing results, effectively making compare function inconsistent.
683 Vector
<ValueStringPair
> values(lengthNotIncludingUndefined
);
684 if (!values
.begin()) {
685 throwOutOfMemoryError(exec
);
689 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++) {
690 JSValuePtr value
= m_storage
->m_vector
[i
];
691 ASSERT(!value
.isUndefined());
692 values
[i
].first
= value
;
695 // FIXME: While calling these toString functions, the array could be mutated.
696 // In that case, objects pointed to by values in this vector might get garbage-collected!
698 // FIXME: The following loop continues to call toString on subsequent values even after
699 // a toString call raises an exception.
701 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
702 values
[i
].second
= values
[i
].first
.toString(exec
);
704 if (exec
->hadException())
707 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
711 mergesort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
713 // FIXME: The qsort library function is likely to not be a stable sort.
714 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
715 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
718 // FIXME: If the toString function changed the length of the array, this might be
719 // modifying the vector incorrectly.
721 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
722 m_storage
->m_vector
[i
] = values
[i
].first
;
724 checkConsistency(SortConsistencyCheck
);
727 struct AVLTreeNodeForArrayCompare
{
730 // Child pointers. The high bit of gt is robbed and used as the
731 // balance factor sign. The high bit of lt is robbed and used as
732 // the magnitude of the balance factor.
737 struct AVLTreeAbstractorForArrayCompare
{
738 typedef int32_t handle
; // Handle is an index into m_nodes vector.
739 typedef JSValuePtr key
;
740 typedef int32_t size
;
742 Vector
<AVLTreeNodeForArrayCompare
> m_nodes
;
744 JSValuePtr m_compareFunction
;
745 CallType m_compareCallType
;
746 const CallData
* m_compareCallData
;
747 JSValuePtr m_globalThisValue
;
749 handle
get_less(handle h
) { return m_nodes
[h
].lt
& 0x7FFFFFFF; }
750 void set_less(handle h
, handle lh
) { m_nodes
[h
].lt
&= 0x80000000; m_nodes
[h
].lt
|= lh
; }
751 handle
get_greater(handle h
) { return m_nodes
[h
].gt
& 0x7FFFFFFF; }
752 void set_greater(handle h
, handle gh
) { m_nodes
[h
].gt
&= 0x80000000; m_nodes
[h
].gt
|= gh
; }
754 int get_balance_factor(handle h
)
756 if (m_nodes
[h
].gt
& 0x80000000)
758 return static_cast<unsigned>(m_nodes
[h
].lt
) >> 31;
761 void set_balance_factor(handle h
, int bf
)
764 m_nodes
[h
].lt
&= 0x7FFFFFFF;
765 m_nodes
[h
].gt
&= 0x7FFFFFFF;
767 m_nodes
[h
].lt
|= 0x80000000;
769 m_nodes
[h
].gt
|= 0x80000000;
771 m_nodes
[h
].gt
&= 0x7FFFFFFF;
775 int compare_key_key(key va
, key vb
)
777 ASSERT(!va
.isUndefined());
778 ASSERT(!vb
.isUndefined());
780 if (m_exec
->hadException())
784 arguments
.append(va
);
785 arguments
.append(vb
);
786 double compareResult
= call(m_exec
, m_compareFunction
, m_compareCallType
, *m_compareCallData
, m_globalThisValue
, arguments
).toNumber(m_exec
);
787 return (compareResult
< 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
790 int compare_key_node(key k
, handle h
) { return compare_key_key(k
, m_nodes
[h
].value
); }
791 int compare_node_node(handle h1
, handle h2
) { return compare_key_key(m_nodes
[h1
].value
, m_nodes
[h2
].value
); }
793 static handle
null() { return 0x7FFFFFFF; }
796 void JSArray::sort(ExecState
* exec
, JSValuePtr compareFunction
, CallType callType
, const CallData
& callData
)
800 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
802 // The maximum tree depth is compiled in - but the caller is clearly up to no good
803 // if a larger array is passed.
804 ASSERT(m_storage
->m_length
<= static_cast<unsigned>(std::numeric_limits
<int>::max()));
805 if (m_storage
->m_length
> static_cast<unsigned>(std::numeric_limits
<int>::max()))
808 if (!m_storage
->m_length
)
811 unsigned usedVectorLength
= min(m_storage
->m_length
, m_storage
->m_vectorLength
);
813 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44> tree
; // Depth 44 is enough for 2^31 items
814 tree
.abstractor().m_exec
= exec
;
815 tree
.abstractor().m_compareFunction
= compareFunction
;
816 tree
.abstractor().m_compareCallType
= callType
;
817 tree
.abstractor().m_compareCallData
= &callData
;
818 tree
.abstractor().m_globalThisValue
= exec
->globalThisValue();
819 tree
.abstractor().m_nodes
.resize(usedVectorLength
+ (m_storage
->m_sparseValueMap
? m_storage
->m_sparseValueMap
->size() : 0));
821 if (!tree
.abstractor().m_nodes
.begin()) {
822 throwOutOfMemoryError(exec
);
826 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
827 // right out from under us while we're building the tree here.
829 unsigned numDefined
= 0;
830 unsigned numUndefined
= 0;
832 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
833 for (; numDefined
< usedVectorLength
; ++numDefined
) {
834 JSValuePtr v
= m_storage
->m_vector
[numDefined
];
835 if (!v
|| v
.isUndefined())
837 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
838 tree
.insert(numDefined
);
840 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
841 JSValuePtr v
= m_storage
->m_vector
[i
];
846 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
847 tree
.insert(numDefined
);
853 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
855 if (SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
) {
856 newUsedVectorLength
+= map
->size();
857 if (newUsedVectorLength
> m_storage
->m_vectorLength
) {
858 // Check that it is possible to allocate an array large enough to hold all the entries.
859 if ((newUsedVectorLength
> MAX_STORAGE_VECTOR_LENGTH
) || !increaseVectorLength(newUsedVectorLength
)) {
860 throwOutOfMemoryError(exec
);
865 SparseArrayValueMap::iterator end
= map
->end();
866 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
) {
867 tree
.abstractor().m_nodes
[numDefined
].value
= it
->second
;
868 tree
.insert(numDefined
);
873 m_storage
->m_sparseValueMap
= 0;
876 ASSERT(tree
.abstractor().m_nodes
.size() >= numDefined
);
878 // FIXME: If the compare function changed the length of the array, the following might be
879 // modifying the vector incorrectly.
881 // Copy the values back into m_storage.
882 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44>::Iterator iter
;
883 iter
.start_iter_least(tree
);
884 for (unsigned i
= 0; i
< numDefined
; ++i
) {
885 m_storage
->m_vector
[i
] = tree
.abstractor().m_nodes
[*iter
].value
;
889 // Put undefined values back in.
890 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
891 m_storage
->m_vector
[i
] = jsUndefined();
893 // Ensure that unused values in the vector are zeroed out.
894 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
895 m_storage
->m_vector
[i
] = noValue();
897 m_fastAccessCutoff
= newUsedVectorLength
;
898 m_storage
->m_numValuesInVector
= newUsedVectorLength
;
900 checkConsistency(SortConsistencyCheck
);
903 void JSArray::fillArgList(ExecState
* exec
, ArgList
& args
)
905 unsigned fastAccessLength
= min(m_storage
->m_length
, m_fastAccessCutoff
);
907 for (; i
< fastAccessLength
; ++i
)
908 args
.append(getIndex(i
));
909 for (; i
< m_storage
->m_length
; ++i
)
910 args
.append(get(exec
, i
));
913 unsigned JSArray::compactForSorting()
917 ArrayStorage
* storage
= m_storage
;
919 unsigned usedVectorLength
= min(m_storage
->m_length
, storage
->m_vectorLength
);
921 unsigned numDefined
= 0;
922 unsigned numUndefined
= 0;
924 for (; numDefined
< usedVectorLength
; ++numDefined
) {
925 JSValuePtr v
= storage
->m_vector
[numDefined
];
926 if (!v
|| v
.isUndefined())
929 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
930 JSValuePtr v
= storage
->m_vector
[i
];
935 storage
->m_vector
[numDefined
++] = v
;
939 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
941 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
942 newUsedVectorLength
+= map
->size();
943 if (newUsedVectorLength
> storage
->m_vectorLength
) {
944 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
945 // exception is thrown by caller.
946 if ((newUsedVectorLength
> MAX_STORAGE_VECTOR_LENGTH
) || !increaseVectorLength(newUsedVectorLength
))
951 SparseArrayValueMap::iterator end
= map
->end();
952 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
)
953 storage
->m_vector
[numDefined
++] = it
->second
;
956 storage
->m_sparseValueMap
= 0;
959 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
960 storage
->m_vector
[i
] = jsUndefined();
961 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
962 storage
->m_vector
[i
] = noValue();
964 m_fastAccessCutoff
= newUsedVectorLength
;
965 storage
->m_numValuesInVector
= newUsedVectorLength
;
967 checkConsistency(SortConsistencyCheck
);
972 void* JSArray::lazyCreationData()
974 return m_storage
->lazyCreationData
;
977 void JSArray::setLazyCreationData(void* d
)
979 m_storage
->lazyCreationData
= d
;
982 #if CHECK_ARRAY_CONSISTENCY
984 void JSArray::checkConsistency(ConsistencyCheckType type
)
987 if (type
== SortConsistencyCheck
)
988 ASSERT(!m_storage
->m_sparseValueMap
);
990 ASSERT(m_fastAccessCutoff
<= m_storage
->m_length
);
991 ASSERT(m_fastAccessCutoff
<= m_storage
->m_numValuesInVector
);
993 unsigned numValuesInVector
= 0;
994 for (unsigned i
= 0; i
< m_storage
->m_vectorLength
; ++i
) {
995 if (JSValuePtr value
= m_storage
->m_vector
[i
]) {
996 ASSERT(i
< m_storage
->m_length
);
997 if (type
!= DestructorConsistencyCheck
)
998 value
->type(); // Likely to crash if the object was deallocated.
1001 ASSERT(i
>= m_fastAccessCutoff
);
1002 if (type
== SortConsistencyCheck
)
1003 ASSERT(i
>= m_storage
->m_numValuesInVector
);
1006 ASSERT(numValuesInVector
== m_storage
->m_numValuesInVector
);
1008 if (m_storage
->m_sparseValueMap
) {
1009 SparseArrayValueMap::iterator end
= m_storage
->m_sparseValueMap
->end();
1010 for (SparseArrayValueMap::iterator it
= m_storage
->m_sparseValueMap
->begin(); it
!= end
; ++it
) {
1011 unsigned index
= it
->first
;
1012 ASSERT(index
< m_storage
->m_length
);
1013 ASSERT(index
>= m_storage
->m_vectorLength
);
1014 ASSERT(index
<= MAX_ARRAY_INDEX
);
1016 if (type
!= DestructorConsistencyCheck
)
1017 it
->second
->type(); // Likely to crash if the object was deallocated.
1024 JSArray
* constructEmptyArray(ExecState
* exec
)
1026 return new (exec
) JSArray(exec
->lexicalGlobalObject()->arrayStructure());
1029 JSArray
* constructEmptyArray(ExecState
* exec
, unsigned initialLength
)
1031 return new (exec
) JSArray(exec
->lexicalGlobalObject()->arrayStructure(), initialLength
);
1034 JSArray
* constructArray(ExecState
* exec
, JSValuePtr singleItemValue
)
1037 values
.append(singleItemValue
);
1038 return new (exec
) JSArray(exec
, exec
->lexicalGlobalObject()->arrayStructure(), values
);
1041 JSArray
* constructArray(ExecState
* exec
, const ArgList
& values
)
1043 return new (exec
) JSArray(exec
, exec
->lexicalGlobalObject()->arrayStructure(), values
);