2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2009 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 "CachedCall.h"
29 #include "Executable.h"
30 #include "PropertyNameArray.h"
31 #include <wtf/AVLTree.h>
32 #include <wtf/Assertions.h>
33 #include <wtf/OwnPtr.h>
34 #include <Operations.h>
36 #define CHECK_ARRAY_CONSISTENCY 0
43 ASSERT_CLASS_FITS_IN_CELL(JSArray
);
45 // Overview of JSArray
47 // Properties of JSArray objects may be stored in one of three locations:
48 // * The regular JSObject property map.
49 // * A storage vector.
50 // * A sparse map of array entries.
52 // Properties with non-numeric identifiers, with identifiers that are not representable
53 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX
54 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
55 // integer) are not considered array indices and will be stored in the JSObject property map.
57 // All properties with a numeric identifer, representable as an unsigned integer i,
58 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
59 // storage vector or the sparse map. An array index i will be handled in the following
62 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector.
63 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
64 // be stored in the storage vector or in the sparse array, depending on the density of
65 // data that would be stored in the vector (a vector being used where at least
66 // (1 / minDensityMultiplier) of the entries would be populated).
67 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
68 // in the sparse array.
70 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
71 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
72 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValue)) +
73 // (vectorLength * sizeof(JSValue)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
74 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue))
76 // These values have to be macros to be used in max() and min() without introducing
77 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
78 #define MIN_SPARSE_ARRAY_INDEX 10000U
79 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
80 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
81 #define MAX_ARRAY_INDEX 0xFFFFFFFEU
83 // Our policy for when to use a vector and when to use a sparse map.
84 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
85 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
86 // as long as it is 1/8 full. If more sparse than that, we use a map.
87 static const unsigned minDensityMultiplier
= 8;
89 const ClassInfo
JSArray::info
= {"Array", 0, 0, 0};
91 static inline size_t storageSize(unsigned vectorLength
)
93 ASSERT(vectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
95 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
96 // - as asserted above - the following calculation cannot overflow.
97 size_t size
= (sizeof(ArrayStorage
) - sizeof(JSValue
)) + (vectorLength
* sizeof(JSValue
));
98 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
99 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
100 ASSERT(((size
- (sizeof(ArrayStorage
) - sizeof(JSValue
))) / sizeof(JSValue
) == vectorLength
) && (size
>= (sizeof(ArrayStorage
) - sizeof(JSValue
))));
105 static inline unsigned increasedVectorLength(unsigned newLength
)
107 ASSERT(newLength
<= MAX_STORAGE_VECTOR_LENGTH
);
109 // Mathematically equivalent to:
110 // increasedLength = (newLength * 3 + 1) / 2;
112 // increasedLength = (unsigned)ceil(newLength * 1.5));
113 // This form is not prone to internal overflow.
114 unsigned increasedLength
= newLength
+ (newLength
>> 1) + (newLength
& 1);
115 ASSERT(increasedLength
>= newLength
);
117 return min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
120 static inline bool isDenseEnoughForVector(unsigned length
, unsigned numValues
)
122 return length
/ minDensityMultiplier
<= numValues
;
125 #if !CHECK_ARRAY_CONSISTENCY
127 inline void JSArray::checkConsistency(ConsistencyCheckType
)
133 JSArray::JSArray(NonNullPassRefPtr
<Structure
> structure
)
134 : JSObject(structure
)
136 unsigned initialCapacity
= 0;
138 m_storage
= static_cast<ArrayStorage
*>(fastZeroedMalloc(storageSize(initialCapacity
)));
139 m_vectorLength
= initialCapacity
;
144 JSArray::JSArray(NonNullPassRefPtr
<Structure
> structure
, unsigned initialLength
)
145 : JSObject(structure
)
147 unsigned initialCapacity
= min(initialLength
, MIN_SPARSE_ARRAY_INDEX
);
149 m_storage
= static_cast<ArrayStorage
*>(fastMalloc(storageSize(initialCapacity
)));
150 m_storage
->m_length
= initialLength
;
151 m_vectorLength
= initialCapacity
;
152 m_storage
->m_numValuesInVector
= 0;
153 m_storage
->m_sparseValueMap
= 0;
154 m_storage
->subclassData
= 0;
155 m_storage
->reportedMapCapacity
= 0;
157 JSValue
* vector
= m_storage
->m_vector
;
158 for (size_t i
= 0; i
< initialCapacity
; ++i
)
159 vector
[i
] = JSValue();
163 Heap::heap(this)->reportExtraMemoryCost(initialCapacity
* sizeof(JSValue
));
166 JSArray::JSArray(NonNullPassRefPtr
<Structure
> structure
, const ArgList
& list
)
167 : JSObject(structure
)
169 unsigned initialCapacity
= list
.size();
171 m_storage
= static_cast<ArrayStorage
*>(fastMalloc(storageSize(initialCapacity
)));
172 m_storage
->m_length
= initialCapacity
;
173 m_vectorLength
= initialCapacity
;
174 m_storage
->m_numValuesInVector
= initialCapacity
;
175 m_storage
->m_sparseValueMap
= 0;
176 m_storage
->subclassData
= 0;
177 m_storage
->reportedMapCapacity
= 0;
180 ArgList::const_iterator end
= list
.end();
181 for (ArgList::const_iterator it
= list
.begin(); it
!= end
; ++it
, ++i
)
182 m_storage
->m_vector
[i
] = *it
;
186 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity
));
191 ASSERT(vptr() == JSGlobalData::jsArrayVPtr
);
192 checkConsistency(DestructorConsistencyCheck
);
194 delete m_storage
->m_sparseValueMap
;
198 bool JSArray::getOwnPropertySlot(ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
200 ArrayStorage
* storage
= m_storage
;
202 if (i
>= storage
->m_length
) {
203 if (i
> MAX_ARRAY_INDEX
)
204 return getOwnPropertySlot(exec
, Identifier::from(exec
, i
), slot
);
208 if (i
< m_vectorLength
) {
209 JSValue
& valueSlot
= storage
->m_vector
[i
];
211 slot
.setValueSlot(&valueSlot
);
214 } else if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
215 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
216 SparseArrayValueMap::iterator it
= map
->find(i
);
217 if (it
!= map
->end()) {
218 slot
.setValueSlot(&it
->second
);
224 return JSObject::getOwnPropertySlot(exec
, Identifier::from(exec
, i
), slot
);
227 bool JSArray::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
229 if (propertyName
== exec
->propertyNames().length
) {
230 slot
.setValue(jsNumber(exec
, length()));
235 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
237 return JSArray::getOwnPropertySlot(exec
, i
, slot
);
239 return JSObject::getOwnPropertySlot(exec
, propertyName
, slot
);
242 bool JSArray::getOwnPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
244 if (propertyName
== exec
->propertyNames().length
) {
245 descriptor
.setDescriptor(jsNumber(exec
, length()), DontDelete
| DontEnum
);
250 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
252 if (i
>= m_storage
->m_length
)
254 if (i
< m_vectorLength
) {
255 JSValue
& value
= m_storage
->m_vector
[i
];
257 descriptor
.setDescriptor(value
, 0);
260 } else if (SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
) {
261 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
262 SparseArrayValueMap::iterator it
= map
->find(i
);
263 if (it
!= map
->end()) {
264 descriptor
.setDescriptor(it
->second
, 0);
270 return JSObject::getOwnPropertyDescriptor(exec
, propertyName
, descriptor
);
274 void JSArray::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
277 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
283 if (propertyName
== exec
->propertyNames().length
) {
284 unsigned newLength
= value
.toUInt32(exec
);
285 if (value
.toNumber(exec
) != static_cast<double>(newLength
)) {
286 throwError(exec
, RangeError
, "Invalid array length.");
289 setLength(newLength
);
293 JSObject::put(exec
, propertyName
, value
, slot
);
296 void JSArray::put(ExecState
* exec
, unsigned i
, JSValue value
)
300 unsigned length
= m_storage
->m_length
;
301 if (i
>= length
&& i
<= MAX_ARRAY_INDEX
) {
303 m_storage
->m_length
= length
;
306 if (i
< m_vectorLength
) {
307 JSValue
& valueSlot
= m_storage
->m_vector
[i
];
314 ++m_storage
->m_numValuesInVector
;
319 putSlowCase(exec
, i
, value
);
322 NEVER_INLINE
void JSArray::putSlowCase(ExecState
* exec
, unsigned i
, JSValue value
)
324 ArrayStorage
* storage
= m_storage
;
325 SparseArrayValueMap
* map
= storage
->m_sparseValueMap
;
327 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
328 if (i
> MAX_ARRAY_INDEX
) {
329 PutPropertySlot slot
;
330 put(exec
, Identifier::from(exec
, i
), value
, slot
);
334 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
335 // (which will only be compacted as we reach indices that are less than MIN_SPARSE_ARRAY_INDEX) - but this makes the check much faster.
336 if ((i
> MAX_STORAGE_VECTOR_INDEX
) || !isDenseEnoughForVector(i
+ 1, storage
->m_numValuesInVector
+ 1)) {
338 map
= new SparseArrayValueMap
;
339 storage
->m_sparseValueMap
= map
;
342 pair
<SparseArrayValueMap::iterator
, bool> result
= map
->add(i
, value
);
343 if (!result
.second
) { // pre-existing entry
344 result
.first
->second
= value
;
348 size_t capacity
= map
->capacity();
349 if (capacity
!= storage
->reportedMapCapacity
) {
350 Heap::heap(this)->reportExtraMemoryCost((capacity
- storage
->reportedMapCapacity
) * (sizeof(unsigned) + sizeof(JSValue
)));
351 storage
->reportedMapCapacity
= capacity
;
357 // We have decided that we'll put the new item into the vector.
358 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
359 if (!map
|| map
->isEmpty()) {
360 if (increaseVectorLength(i
+ 1)) {
362 storage
->m_vector
[i
] = value
;
363 ++storage
->m_numValuesInVector
;
366 throwOutOfMemoryError(exec
);
370 // Decide how many values it would be best to move from the map.
371 unsigned newNumValuesInVector
= storage
->m_numValuesInVector
+ 1;
372 unsigned newVectorLength
= increasedVectorLength(i
+ 1);
373 for (unsigned j
= max(m_vectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< newVectorLength
; ++j
)
374 newNumValuesInVector
+= map
->contains(j
);
375 if (i
>= MIN_SPARSE_ARRAY_INDEX
)
376 newNumValuesInVector
-= map
->contains(i
);
377 if (isDenseEnoughForVector(newVectorLength
, newNumValuesInVector
)) {
378 unsigned proposedNewNumValuesInVector
= newNumValuesInVector
;
379 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
380 while (newVectorLength
< MAX_STORAGE_VECTOR_LENGTH
) {
381 unsigned proposedNewVectorLength
= increasedVectorLength(newVectorLength
+ 1);
382 for (unsigned j
= max(newVectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< proposedNewVectorLength
; ++j
)
383 proposedNewNumValuesInVector
+= map
->contains(j
);
384 if (!isDenseEnoughForVector(proposedNewVectorLength
, proposedNewNumValuesInVector
))
386 newVectorLength
= proposedNewVectorLength
;
387 newNumValuesInVector
= proposedNewNumValuesInVector
;
391 if (!tryFastRealloc(storage
, storageSize(newVectorLength
)).getValue(storage
)) {
392 throwOutOfMemoryError(exec
);
396 unsigned vectorLength
= m_vectorLength
;
398 if (newNumValuesInVector
== storage
->m_numValuesInVector
+ 1) {
399 for (unsigned j
= vectorLength
; j
< newVectorLength
; ++j
)
400 storage
->m_vector
[j
] = JSValue();
401 if (i
> MIN_SPARSE_ARRAY_INDEX
)
404 for (unsigned j
= vectorLength
; j
< max(vectorLength
, MIN_SPARSE_ARRAY_INDEX
); ++j
)
405 storage
->m_vector
[j
] = JSValue();
406 for (unsigned j
= max(vectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< newVectorLength
; ++j
)
407 storage
->m_vector
[j
] = map
->take(j
);
410 storage
->m_vector
[i
] = value
;
412 m_vectorLength
= newVectorLength
;
413 storage
->m_numValuesInVector
= newNumValuesInVector
;
419 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
422 bool JSArray::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
425 unsigned i
= propertyName
.toArrayIndex(&isArrayIndex
);
427 return deleteProperty(exec
, i
);
429 if (propertyName
== exec
->propertyNames().length
)
432 return JSObject::deleteProperty(exec
, propertyName
);
435 bool JSArray::deleteProperty(ExecState
* exec
, unsigned i
)
439 ArrayStorage
* storage
= m_storage
;
441 if (i
< m_vectorLength
) {
442 JSValue
& valueSlot
= storage
->m_vector
[i
];
447 valueSlot
= JSValue();
448 --storage
->m_numValuesInVector
;
453 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
454 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
455 SparseArrayValueMap::iterator it
= map
->find(i
);
456 if (it
!= map
->end()) {
466 if (i
> MAX_ARRAY_INDEX
)
467 return deleteProperty(exec
, Identifier::from(exec
, i
));
472 void JSArray::getOwnPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
474 // FIXME: Filling PropertyNameArray with an identifier for every integer
475 // is incredibly inefficient for large arrays. We need a different approach,
476 // which almost certainly means a different structure for PropertyNameArray.
478 ArrayStorage
* storage
= m_storage
;
480 unsigned usedVectorLength
= min(storage
->m_length
, m_vectorLength
);
481 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
482 if (storage
->m_vector
[i
])
483 propertyNames
.add(Identifier::from(exec
, i
));
486 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
487 SparseArrayValueMap::iterator end
= map
->end();
488 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
)
489 propertyNames
.add(Identifier::from(exec
, it
->first
));
492 if (mode
== IncludeDontEnumProperties
)
493 propertyNames
.add(exec
->propertyNames().length
);
495 JSObject::getOwnPropertyNames(exec
, propertyNames
, mode
);
498 bool JSArray::increaseVectorLength(unsigned newLength
)
500 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
501 // to the vector. Callers have to account for that, because they can do it more efficiently.
503 ArrayStorage
* storage
= m_storage
;
505 unsigned vectorLength
= m_vectorLength
;
506 ASSERT(newLength
> vectorLength
);
507 ASSERT(newLength
<= MAX_STORAGE_VECTOR_INDEX
);
508 unsigned newVectorLength
= increasedVectorLength(newLength
);
510 if (!tryFastRealloc(storage
, storageSize(newVectorLength
)).getValue(storage
))
513 m_vectorLength
= newVectorLength
;
515 for (unsigned i
= vectorLength
; i
< newVectorLength
; ++i
)
516 storage
->m_vector
[i
] = JSValue();
520 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
525 void JSArray::setLength(unsigned newLength
)
529 ArrayStorage
* storage
= m_storage
;
531 unsigned length
= m_storage
->m_length
;
533 if (newLength
< length
) {
534 unsigned usedVectorLength
= min(length
, m_vectorLength
);
535 for (unsigned i
= newLength
; i
< usedVectorLength
; ++i
) {
536 JSValue
& valueSlot
= storage
->m_vector
[i
];
537 bool hadValue
= valueSlot
;
538 valueSlot
= JSValue();
539 storage
->m_numValuesInVector
-= hadValue
;
542 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
543 SparseArrayValueMap copy
= *map
;
544 SparseArrayValueMap::iterator end
= copy
.end();
545 for (SparseArrayValueMap::iterator it
= copy
.begin(); it
!= end
; ++it
) {
546 if (it
->first
>= newLength
)
547 map
->remove(it
->first
);
549 if (map
->isEmpty()) {
551 storage
->m_sparseValueMap
= 0;
556 m_storage
->m_length
= newLength
;
561 JSValue
JSArray::pop()
565 unsigned length
= m_storage
->m_length
;
567 return jsUndefined();
573 if (length
< m_vectorLength
) {
574 JSValue
& valueSlot
= m_storage
->m_vector
[length
];
576 --m_storage
->m_numValuesInVector
;
578 valueSlot
= JSValue();
580 result
= jsUndefined();
582 result
= jsUndefined();
583 if (SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
) {
584 SparseArrayValueMap::iterator it
= map
->find(length
);
585 if (it
!= map
->end()) {
588 if (map
->isEmpty()) {
590 m_storage
->m_sparseValueMap
= 0;
596 m_storage
->m_length
= length
;
603 void JSArray::push(ExecState
* exec
, JSValue value
)
607 if (m_storage
->m_length
< m_vectorLength
) {
608 m_storage
->m_vector
[m_storage
->m_length
] = value
;
609 ++m_storage
->m_numValuesInVector
;
610 ++m_storage
->m_length
;
615 if (m_storage
->m_length
< MIN_SPARSE_ARRAY_INDEX
) {
616 SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
;
617 if (!map
|| map
->isEmpty()) {
618 if (increaseVectorLength(m_storage
->m_length
+ 1)) {
619 m_storage
->m_vector
[m_storage
->m_length
] = value
;
620 ++m_storage
->m_numValuesInVector
;
621 ++m_storage
->m_length
;
626 throwOutOfMemoryError(exec
);
631 putSlowCase(exec
, m_storage
->m_length
++, value
);
634 void JSArray::markChildren(MarkStack
& markStack
)
636 markChildrenDirect(markStack
);
639 static int compareNumbersForQSort(const void* a
, const void* b
)
641 double da
= static_cast<const JSValue
*>(a
)->uncheckedGetNumber();
642 double db
= static_cast<const JSValue
*>(b
)->uncheckedGetNumber();
643 return (da
> db
) - (da
< db
);
646 static int compareByStringPairForQSort(const void* a
, const void* b
)
648 const ValueStringPair
* va
= static_cast<const ValueStringPair
*>(a
);
649 const ValueStringPair
* vb
= static_cast<const ValueStringPair
*>(b
);
650 return compare(va
->second
, vb
->second
);
653 void JSArray::sortNumeric(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
655 unsigned lengthNotIncludingUndefined
= compactForSorting();
656 if (m_storage
->m_sparseValueMap
) {
657 throwOutOfMemoryError(exec
);
661 if (!lengthNotIncludingUndefined
)
664 bool allValuesAreNumbers
= true;
665 size_t size
= m_storage
->m_numValuesInVector
;
666 for (size_t i
= 0; i
< size
; ++i
) {
667 if (!m_storage
->m_vector
[i
].isNumber()) {
668 allValuesAreNumbers
= false;
673 if (!allValuesAreNumbers
)
674 return sort(exec
, compareFunction
, callType
, callData
);
676 // For numeric comparison, which is fast, qsort is faster than mergesort. We
677 // also don't require mergesort's stability, since there's no user visible
678 // side-effect from swapping the order of equal primitive values.
679 qsort(m_storage
->m_vector
, size
, sizeof(JSValue
), compareNumbersForQSort
);
681 checkConsistency(SortConsistencyCheck
);
684 void JSArray::sort(ExecState
* exec
)
686 unsigned lengthNotIncludingUndefined
= compactForSorting();
687 if (m_storage
->m_sparseValueMap
) {
688 throwOutOfMemoryError(exec
);
692 if (!lengthNotIncludingUndefined
)
695 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
696 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
697 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
698 // random or otherwise changing results, effectively making compare function inconsistent.
700 Vector
<ValueStringPair
> values(lengthNotIncludingUndefined
);
701 if (!values
.begin()) {
702 throwOutOfMemoryError(exec
);
706 Heap::heap(this)->pushTempSortVector(&values
);
708 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++) {
709 JSValue value
= m_storage
->m_vector
[i
];
710 ASSERT(!value
.isUndefined());
711 values
[i
].first
= value
;
714 // FIXME: The following loop continues to call toString on subsequent values even after
715 // a toString call raises an exception.
717 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
718 values
[i
].second
= values
[i
].first
.toString(exec
);
720 if (exec
->hadException()) {
721 Heap::heap(this)->popTempSortVector(&values
);
725 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
729 mergesort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
731 // FIXME: The qsort library function is likely to not be a stable sort.
732 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
733 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
736 // If the toString function changed the length of the array or vector storage,
737 // increase the length to handle the orignal number of actual values.
738 if (m_vectorLength
< lengthNotIncludingUndefined
)
739 increaseVectorLength(lengthNotIncludingUndefined
);
740 if (m_storage
->m_length
< lengthNotIncludingUndefined
)
741 m_storage
->m_length
= lengthNotIncludingUndefined
;
743 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
744 m_storage
->m_vector
[i
] = values
[i
].first
;
746 Heap::heap(this)->popTempSortVector(&values
);
748 checkConsistency(SortConsistencyCheck
);
751 struct AVLTreeNodeForArrayCompare
{
754 // Child pointers. The high bit of gt is robbed and used as the
755 // balance factor sign. The high bit of lt is robbed and used as
756 // the magnitude of the balance factor.
761 struct AVLTreeAbstractorForArrayCompare
{
762 typedef int32_t handle
; // Handle is an index into m_nodes vector.
764 typedef int32_t size
;
766 Vector
<AVLTreeNodeForArrayCompare
> m_nodes
;
768 JSValue m_compareFunction
;
769 CallType m_compareCallType
;
770 const CallData
* m_compareCallData
;
771 JSValue m_globalThisValue
;
772 OwnPtr
<CachedCall
> m_cachedCall
;
774 handle
get_less(handle h
) { return m_nodes
[h
].lt
& 0x7FFFFFFF; }
775 void set_less(handle h
, handle lh
) { m_nodes
[h
].lt
&= 0x80000000; m_nodes
[h
].lt
|= lh
; }
776 handle
get_greater(handle h
) { return m_nodes
[h
].gt
& 0x7FFFFFFF; }
777 void set_greater(handle h
, handle gh
) { m_nodes
[h
].gt
&= 0x80000000; m_nodes
[h
].gt
|= gh
; }
779 int get_balance_factor(handle h
)
781 if (m_nodes
[h
].gt
& 0x80000000)
783 return static_cast<unsigned>(m_nodes
[h
].lt
) >> 31;
786 void set_balance_factor(handle h
, int bf
)
789 m_nodes
[h
].lt
&= 0x7FFFFFFF;
790 m_nodes
[h
].gt
&= 0x7FFFFFFF;
792 m_nodes
[h
].lt
|= 0x80000000;
794 m_nodes
[h
].gt
|= 0x80000000;
796 m_nodes
[h
].gt
&= 0x7FFFFFFF;
800 int compare_key_key(key va
, key vb
)
802 ASSERT(!va
.isUndefined());
803 ASSERT(!vb
.isUndefined());
805 if (m_exec
->hadException())
808 double compareResult
;
810 m_cachedCall
->setThis(m_globalThisValue
);
811 m_cachedCall
->setArgument(0, va
);
812 m_cachedCall
->setArgument(1, vb
);
813 compareResult
= m_cachedCall
->call().toNumber(m_cachedCall
->newCallFrame(m_exec
));
815 MarkedArgumentBuffer arguments
;
816 arguments
.append(va
);
817 arguments
.append(vb
);
818 compareResult
= call(m_exec
, m_compareFunction
, m_compareCallType
, *m_compareCallData
, m_globalThisValue
, arguments
).toNumber(m_exec
);
820 return (compareResult
< 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
823 int compare_key_node(key k
, handle h
) { return compare_key_key(k
, m_nodes
[h
].value
); }
824 int compare_node_node(handle h1
, handle h2
) { return compare_key_key(m_nodes
[h1
].value
, m_nodes
[h2
].value
); }
826 static handle
null() { return 0x7FFFFFFF; }
829 void JSArray::sort(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
833 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
835 // The maximum tree depth is compiled in - but the caller is clearly up to no good
836 // if a larger array is passed.
837 ASSERT(m_storage
->m_length
<= static_cast<unsigned>(std::numeric_limits
<int>::max()));
838 if (m_storage
->m_length
> static_cast<unsigned>(std::numeric_limits
<int>::max()))
841 if (!m_storage
->m_length
)
844 unsigned usedVectorLength
= min(m_storage
->m_length
, m_vectorLength
);
846 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44> tree
; // Depth 44 is enough for 2^31 items
847 tree
.abstractor().m_exec
= exec
;
848 tree
.abstractor().m_compareFunction
= compareFunction
;
849 tree
.abstractor().m_compareCallType
= callType
;
850 tree
.abstractor().m_compareCallData
= &callData
;
851 tree
.abstractor().m_globalThisValue
= exec
->globalThisValue();
852 tree
.abstractor().m_nodes
.resize(usedVectorLength
+ (m_storage
->m_sparseValueMap
? m_storage
->m_sparseValueMap
->size() : 0));
854 if (callType
== CallTypeJS
)
855 tree
.abstractor().m_cachedCall
.set(new CachedCall(exec
, asFunction(compareFunction
), 2, exec
->exceptionSlot()));
857 if (!tree
.abstractor().m_nodes
.begin()) {
858 throwOutOfMemoryError(exec
);
862 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
863 // right out from under us while we're building the tree here.
865 unsigned numDefined
= 0;
866 unsigned numUndefined
= 0;
868 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
869 for (; numDefined
< usedVectorLength
; ++numDefined
) {
870 JSValue v
= m_storage
->m_vector
[numDefined
];
871 if (!v
|| v
.isUndefined())
873 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
874 tree
.insert(numDefined
);
876 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
877 JSValue v
= m_storage
->m_vector
[i
];
882 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
883 tree
.insert(numDefined
);
889 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
891 if (SparseArrayValueMap
* map
= m_storage
->m_sparseValueMap
) {
892 newUsedVectorLength
+= map
->size();
893 if (newUsedVectorLength
> m_vectorLength
) {
894 // Check that it is possible to allocate an array large enough to hold all the entries.
895 if ((newUsedVectorLength
> MAX_STORAGE_VECTOR_LENGTH
) || !increaseVectorLength(newUsedVectorLength
)) {
896 throwOutOfMemoryError(exec
);
901 SparseArrayValueMap::iterator end
= map
->end();
902 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
) {
903 tree
.abstractor().m_nodes
[numDefined
].value
= it
->second
;
904 tree
.insert(numDefined
);
909 m_storage
->m_sparseValueMap
= 0;
912 ASSERT(tree
.abstractor().m_nodes
.size() >= numDefined
);
914 // FIXME: If the compare function changed the length of the array, the following might be
915 // modifying the vector incorrectly.
917 // Copy the values back into m_storage.
918 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44>::Iterator iter
;
919 iter
.start_iter_least(tree
);
920 for (unsigned i
= 0; i
< numDefined
; ++i
) {
921 m_storage
->m_vector
[i
] = tree
.abstractor().m_nodes
[*iter
].value
;
925 // Put undefined values back in.
926 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
927 m_storage
->m_vector
[i
] = jsUndefined();
929 // Ensure that unused values in the vector are zeroed out.
930 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
931 m_storage
->m_vector
[i
] = JSValue();
933 m_storage
->m_numValuesInVector
= newUsedVectorLength
;
935 checkConsistency(SortConsistencyCheck
);
938 void JSArray::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
940 JSValue
* vector
= m_storage
->m_vector
;
941 unsigned vectorEnd
= min(m_storage
->m_length
, m_vectorLength
);
943 for (; i
< vectorEnd
; ++i
) {
944 JSValue
& v
= vector
[i
];
950 for (; i
< m_storage
->m_length
; ++i
)
951 args
.append(get(exec
, i
));
954 void JSArray::copyToRegisters(ExecState
* exec
, Register
* buffer
, uint32_t maxSize
)
956 ASSERT(m_storage
->m_length
>= maxSize
);
957 UNUSED_PARAM(maxSize
);
958 JSValue
* vector
= m_storage
->m_vector
;
959 unsigned vectorEnd
= min(maxSize
, m_vectorLength
);
961 for (; i
< vectorEnd
; ++i
) {
962 JSValue
& v
= vector
[i
];
968 for (; i
< maxSize
; ++i
)
969 buffer
[i
] = get(exec
, i
);
972 unsigned JSArray::compactForSorting()
976 ArrayStorage
* storage
= m_storage
;
978 unsigned usedVectorLength
= min(m_storage
->m_length
, m_vectorLength
);
980 unsigned numDefined
= 0;
981 unsigned numUndefined
= 0;
983 for (; numDefined
< usedVectorLength
; ++numDefined
) {
984 JSValue v
= storage
->m_vector
[numDefined
];
985 if (!v
|| v
.isUndefined())
988 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
989 JSValue v
= storage
->m_vector
[i
];
994 storage
->m_vector
[numDefined
++] = v
;
998 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1000 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
1001 newUsedVectorLength
+= map
->size();
1002 if (newUsedVectorLength
> m_vectorLength
) {
1003 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
1004 // exception is thrown by caller.
1005 if ((newUsedVectorLength
> MAX_STORAGE_VECTOR_LENGTH
) || !increaseVectorLength(newUsedVectorLength
))
1007 storage
= m_storage
;
1010 SparseArrayValueMap::iterator end
= map
->end();
1011 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
)
1012 storage
->m_vector
[numDefined
++] = it
->second
;
1015 storage
->m_sparseValueMap
= 0;
1018 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
1019 storage
->m_vector
[i
] = jsUndefined();
1020 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
1021 storage
->m_vector
[i
] = JSValue();
1023 storage
->m_numValuesInVector
= newUsedVectorLength
;
1025 checkConsistency(SortConsistencyCheck
);
1030 void* JSArray::subclassData() const
1032 return m_storage
->subclassData
;
1035 void JSArray::setSubclassData(void* d
)
1037 m_storage
->subclassData
= d
;
1040 #if CHECK_ARRAY_CONSISTENCY
1042 void JSArray::checkConsistency(ConsistencyCheckType type
)
1045 if (type
== SortConsistencyCheck
)
1046 ASSERT(!m_storage
->m_sparseValueMap
);
1048 unsigned numValuesInVector
= 0;
1049 for (unsigned i
= 0; i
< m_vectorLength
; ++i
) {
1050 if (JSValue value
= m_storage
->m_vector
[i
]) {
1051 ASSERT(i
< m_storage
->m_length
);
1052 if (type
!= DestructorConsistencyCheck
)
1053 value
->type(); // Likely to crash if the object was deallocated.
1054 ++numValuesInVector
;
1056 if (type
== SortConsistencyCheck
)
1057 ASSERT(i
>= m_storage
->m_numValuesInVector
);
1060 ASSERT(numValuesInVector
== m_storage
->m_numValuesInVector
);
1061 ASSERT(numValuesInVector
<= m_storage
->m_length
);
1063 if (m_storage
->m_sparseValueMap
) {
1064 SparseArrayValueMap::iterator end
= m_storage
->m_sparseValueMap
->end();
1065 for (SparseArrayValueMap::iterator it
= m_storage
->m_sparseValueMap
->begin(); it
!= end
; ++it
) {
1066 unsigned index
= it
->first
;
1067 ASSERT(index
< m_storage
->m_length
);
1068 ASSERT(index
>= m_vectorLength
);
1069 ASSERT(index
<= MAX_ARRAY_INDEX
);
1071 if (type
!= DestructorConsistencyCheck
)
1072 it
->second
->type(); // Likely to crash if the object was deallocated.