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>
41 ASSERT_CLASS_FITS_IN_CELL(JSArray
);
43 // Overview of JSArray
45 // Properties of JSArray objects may be stored in one of three locations:
46 // * The regular JSObject property map.
47 // * A storage vector.
48 // * A sparse map of array entries.
50 // Properties with non-numeric identifiers, with identifiers that are not representable
51 // as an unsigned integer, or where the value is greater than MAX_ARRAY_INDEX
52 // (specifically, this is only one property - the value 0xFFFFFFFFU as an unsigned 32-bit
53 // integer) are not considered array indices and will be stored in the JSObject property map.
55 // All properties with a numeric identifer, representable as an unsigned integer i,
56 // where (i <= MAX_ARRAY_INDEX), are an array index and will be stored in either the
57 // storage vector or the sparse map. An array index i will be handled in the following
60 // * Where (i < MIN_SPARSE_ARRAY_INDEX) the value will be stored in the storage vector.
61 // * Where (MIN_SPARSE_ARRAY_INDEX <= i <= MAX_STORAGE_VECTOR_INDEX) the value will either
62 // be stored in the storage vector or in the sparse array, depending on the density of
63 // data that would be stored in the vector (a vector being used where at least
64 // (1 / minDensityMultiplier) of the entries would be populated).
65 // * Where (MAX_STORAGE_VECTOR_INDEX < i <= MAX_ARRAY_INDEX) the value will always be stored
66 // in the sparse array.
68 // The definition of MAX_STORAGE_VECTOR_LENGTH is dependant on the definition storageSize
69 // function below - the MAX_STORAGE_VECTOR_LENGTH limit is defined such that the storage
70 // size calculation cannot overflow. (sizeof(ArrayStorage) - sizeof(JSValue)) +
71 // (vectorLength * sizeof(JSValue)) must be <= 0xFFFFFFFFU (which is maximum value of size_t).
72 #define MAX_STORAGE_VECTOR_LENGTH static_cast<unsigned>((0xFFFFFFFFU - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue))
74 // These values have to be macros to be used in max() and min() without introducing
75 // a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
76 #define MIN_SPARSE_ARRAY_INDEX 10000U
77 #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
78 // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
79 #define MAX_ARRAY_INDEX 0xFFFFFFFEU
81 // The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate
82 // for an array that was created with a sepcified length (e.g. a = new Array(123))
83 #define BASE_VECTOR_LEN 4U
85 // The upper bound to the size we'll grow a zero length array when the first element
87 #define FIRST_VECTOR_GROW 4U
89 // Our policy for when to use a vector and when to use a sparse map.
90 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
91 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
92 // as long as it is 1/8 full. If more sparse than that, we use a map.
93 static const unsigned minDensityMultiplier
= 8;
95 const ClassInfo
JSArray::s_info
= {"Array", &JSNonFinalObject::s_info
, 0, 0};
97 // We keep track of the size of the last array after it was grown. We use this
98 // as a simple heuristic for as the value to grow the next array from size 0.
99 // This value is capped by the constant FIRST_VECTOR_GROW defined above.
100 static unsigned lastArraySize
= 0;
102 static inline size_t storageSize(unsigned vectorLength
)
104 ASSERT(vectorLength
<= MAX_STORAGE_VECTOR_LENGTH
);
106 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
107 // - as asserted above - the following calculation cannot overflow.
108 size_t size
= (sizeof(ArrayStorage
) - sizeof(JSValue
)) + (vectorLength
* sizeof(JSValue
));
109 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
110 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
111 ASSERT(((size
- (sizeof(ArrayStorage
) - sizeof(JSValue
))) / sizeof(JSValue
) == vectorLength
) && (size
>= (sizeof(ArrayStorage
) - sizeof(JSValue
))));
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(VPtrStealingHackType
)
130 : JSNonFinalObject(VPtrStealingHack
)
134 JSArray::JSArray(JSGlobalData
& globalData
, Structure
* structure
)
135 : JSNonFinalObject(globalData
, structure
)
137 ASSERT(inherits(&s_info
));
139 unsigned initialCapacity
= 0;
141 m_storage
= static_cast<ArrayStorage
*>(fastZeroedMalloc(storageSize(initialCapacity
)));
142 m_storage
->m_allocBase
= m_storage
;
144 m_vectorLength
= initialCapacity
;
148 Heap::heap(this)->reportExtraMemoryCost(storageSize(0));
151 JSArray::JSArray(JSGlobalData
& globalData
, Structure
* structure
, unsigned initialLength
, ArrayCreationMode creationMode
)
152 : JSNonFinalObject(globalData
, structure
)
154 ASSERT(inherits(&s_info
));
156 unsigned initialCapacity
;
157 if (creationMode
== CreateCompact
)
158 initialCapacity
= initialLength
;
160 initialCapacity
= min(BASE_VECTOR_LEN
, MIN_SPARSE_ARRAY_INDEX
);
162 m_storage
= static_cast<ArrayStorage
*>(fastMalloc(storageSize(initialCapacity
)));
163 m_storage
->m_allocBase
= m_storage
;
164 m_storage
->m_length
= initialLength
;
166 m_vectorLength
= initialCapacity
;
167 m_storage
->m_sparseValueMap
= 0;
168 m_storage
->subclassData
= 0;
169 m_storage
->reportedMapCapacity
= 0;
171 if (creationMode
== CreateCompact
) {
172 #if CHECK_ARRAY_CONSISTENCY
173 m_storage
->m_inCompactInitialization
= !!initialCapacity
;
175 m_storage
->m_length
= 0;
176 m_storage
->m_numValuesInVector
= initialCapacity
;
178 #if CHECK_ARRAY_CONSISTENCY
179 storage
->m_inCompactInitialization
= false;
181 m_storage
->m_length
= initialLength
;
182 m_storage
->m_numValuesInVector
= 0;
183 WriteBarrier
<Unknown
>* vector
= m_storage
->m_vector
;
184 for (size_t i
= 0; i
< initialCapacity
; ++i
)
190 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity
));
193 JSArray::JSArray(JSGlobalData
& globalData
, Structure
* structure
, const ArgList
& list
)
194 : JSNonFinalObject(globalData
, structure
)
196 ASSERT(inherits(&s_info
));
198 unsigned initialCapacity
= list
.size();
199 unsigned initialStorage
;
201 // If the ArgList is empty, allocate space for 3 entries. This value empirically
202 // works well for benchmarks.
203 if (!initialCapacity
)
206 initialStorage
= initialCapacity
;
208 m_storage
= static_cast<ArrayStorage
*>(fastMalloc(storageSize(initialStorage
)));
209 m_storage
->m_allocBase
= m_storage
;
211 m_storage
->m_length
= initialCapacity
;
212 m_vectorLength
= initialStorage
;
213 m_storage
->m_numValuesInVector
= initialCapacity
;
214 m_storage
->m_sparseValueMap
= 0;
215 m_storage
->subclassData
= 0;
216 m_storage
->reportedMapCapacity
= 0;
217 #if CHECK_ARRAY_CONSISTENCY
218 m_storage
->m_inCompactInitialization
= false;
222 WriteBarrier
<Unknown
>* vector
= m_storage
->m_vector
;
223 ArgList::const_iterator end
= list
.end();
224 for (ArgList::const_iterator it
= list
.begin(); it
!= end
; ++it
, ++i
)
225 vector
[i
].set(globalData
, this, *it
);
226 for (; i
< initialStorage
; i
++)
231 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialStorage
));
236 ASSERT(vptr() == JSGlobalData::jsArrayVPtr
);
237 checkConsistency(DestructorConsistencyCheck
);
239 delete m_storage
->m_sparseValueMap
;
240 fastFree(m_storage
->m_allocBase
);
243 bool JSArray::getOwnPropertySlot(ExecState
* exec
, unsigned i
, PropertySlot
& slot
)
245 ArrayStorage
* storage
= m_storage
;
247 if (i
>= storage
->m_length
) {
248 if (i
> MAX_ARRAY_INDEX
)
249 return getOwnPropertySlot(exec
, Identifier::from(exec
, i
), slot
);
253 if (i
< m_vectorLength
) {
254 JSValue value
= storage
->m_vector
[i
].get();
256 slot
.setValue(value
);
259 } else if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
260 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
261 SparseArrayValueMap::iterator it
= map
->find(i
);
262 if (it
!= map
->end()) {
263 slot
.setValue(it
->second
.get());
269 return JSObject::getOwnPropertySlot(exec
, Identifier::from(exec
, i
), slot
);
272 bool JSArray::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
274 if (propertyName
== exec
->propertyNames().length
) {
275 slot
.setValue(jsNumber(length()));
280 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
282 return JSArray::getOwnPropertySlot(exec
, i
, slot
);
284 return JSObject::getOwnPropertySlot(exec
, propertyName
, slot
);
287 bool JSArray::getOwnPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
289 if (propertyName
== exec
->propertyNames().length
) {
290 descriptor
.setDescriptor(jsNumber(length()), DontDelete
| DontEnum
);
294 ArrayStorage
* storage
= m_storage
;
297 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
299 if (i
>= storage
->m_length
)
301 if (i
< m_vectorLength
) {
302 WriteBarrier
<Unknown
>& value
= storage
->m_vector
[i
];
304 descriptor
.setDescriptor(value
.get(), 0);
307 } else if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
308 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
309 SparseArrayValueMap::iterator it
= map
->find(i
);
310 if (it
!= map
->end()) {
311 descriptor
.setDescriptor(it
->second
.get(), 0);
317 return JSObject::getOwnPropertyDescriptor(exec
, propertyName
, descriptor
);
321 void JSArray::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
324 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
330 if (propertyName
== exec
->propertyNames().length
) {
331 unsigned newLength
= value
.toUInt32(exec
);
332 if (value
.toNumber(exec
) != static_cast<double>(newLength
)) {
333 throwError(exec
, createRangeError(exec
, "Invalid array length."));
336 setLength(newLength
);
340 JSObject::put(exec
, propertyName
, value
, slot
);
343 void JSArray::put(ExecState
* exec
, unsigned i
, JSValue value
)
347 ArrayStorage
* storage
= m_storage
;
349 unsigned length
= storage
->m_length
;
350 if (i
>= length
&& i
<= MAX_ARRAY_INDEX
) {
352 storage
->m_length
= length
;
355 if (i
< m_vectorLength
) {
356 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
358 valueSlot
.set(exec
->globalData(), this, value
);
362 valueSlot
.set(exec
->globalData(), this, value
);
363 ++storage
->m_numValuesInVector
;
368 putSlowCase(exec
, i
, value
);
371 NEVER_INLINE
void JSArray::putSlowCase(ExecState
* exec
, unsigned i
, JSValue value
)
373 ArrayStorage
* storage
= m_storage
;
375 SparseArrayValueMap
* map
= storage
->m_sparseValueMap
;
377 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
378 if (i
> MAX_ARRAY_INDEX
) {
379 PutPropertySlot slot
;
380 put(exec
, Identifier::from(exec
, i
), value
, slot
);
384 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
385 // (which will only be compacted as we reach indices that are less than MIN_SPARSE_ARRAY_INDEX) - but this makes the check much faster.
386 if ((i
> MAX_STORAGE_VECTOR_INDEX
) || !isDenseEnoughForVector(i
+ 1, storage
->m_numValuesInVector
+ 1)) {
388 map
= new SparseArrayValueMap
;
389 storage
->m_sparseValueMap
= map
;
392 WriteBarrier
<Unknown
> temp
;
393 pair
<SparseArrayValueMap::iterator
, bool> result
= map
->add(i
, temp
);
394 result
.first
->second
.set(exec
->globalData(), this, value
);
395 if (!result
.second
) // pre-existing entry
398 size_t capacity
= map
->capacity();
399 if (capacity
!= storage
->reportedMapCapacity
) {
400 Heap::heap(this)->reportExtraMemoryCost((capacity
- storage
->reportedMapCapacity
) * (sizeof(unsigned) + sizeof(JSValue
)));
401 storage
->reportedMapCapacity
= capacity
;
407 // We have decided that we'll put the new item into the vector.
408 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
409 if (!map
|| map
->isEmpty()) {
410 if (increaseVectorLength(i
+ 1)) {
412 storage
->m_vector
[i
].set(exec
->globalData(), this, value
);
413 ++storage
->m_numValuesInVector
;
416 throwOutOfMemoryError(exec
);
420 // Decide how many values it would be best to move from the map.
421 unsigned newNumValuesInVector
= storage
->m_numValuesInVector
+ 1;
422 unsigned newVectorLength
= getNewVectorLength(i
+ 1);
423 for (unsigned j
= max(m_vectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< newVectorLength
; ++j
)
424 newNumValuesInVector
+= map
->contains(j
);
425 if (i
>= MIN_SPARSE_ARRAY_INDEX
)
426 newNumValuesInVector
-= map
->contains(i
);
427 if (isDenseEnoughForVector(newVectorLength
, newNumValuesInVector
)) {
428 unsigned needLength
= max(i
+ 1, storage
->m_length
);
429 unsigned proposedNewNumValuesInVector
= newNumValuesInVector
;
430 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
431 while ((newVectorLength
< needLength
) && (newVectorLength
< MAX_STORAGE_VECTOR_LENGTH
)) {
432 unsigned proposedNewVectorLength
= getNewVectorLength(newVectorLength
+ 1);
433 for (unsigned j
= max(newVectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< proposedNewVectorLength
; ++j
)
434 proposedNewNumValuesInVector
+= map
->contains(j
);
435 if (!isDenseEnoughForVector(proposedNewVectorLength
, proposedNewNumValuesInVector
))
437 newVectorLength
= proposedNewVectorLength
;
438 newNumValuesInVector
= proposedNewNumValuesInVector
;
442 void* baseStorage
= storage
->m_allocBase
;
444 if (!tryFastRealloc(baseStorage
, storageSize(newVectorLength
+ m_indexBias
)).getValue(baseStorage
)) {
445 throwOutOfMemoryError(exec
);
449 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(static_cast<char*>(baseStorage
) + m_indexBias
* sizeof(JSValue
));
450 m_storage
->m_allocBase
= baseStorage
;
453 unsigned vectorLength
= m_vectorLength
;
454 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
456 if (newNumValuesInVector
== storage
->m_numValuesInVector
+ 1) {
457 for (unsigned j
= vectorLength
; j
< newVectorLength
; ++j
)
459 if (i
> MIN_SPARSE_ARRAY_INDEX
)
462 for (unsigned j
= vectorLength
; j
< max(vectorLength
, MIN_SPARSE_ARRAY_INDEX
); ++j
)
464 JSGlobalData
& globalData
= exec
->globalData();
465 for (unsigned j
= max(vectorLength
, MIN_SPARSE_ARRAY_INDEX
); j
< newVectorLength
; ++j
)
466 vector
[j
].set(globalData
, this, map
->take(j
).get());
469 ASSERT(i
< newVectorLength
);
471 m_vectorLength
= newVectorLength
;
472 storage
->m_numValuesInVector
= newNumValuesInVector
;
474 storage
->m_vector
[i
].set(exec
->globalData(), this, value
);
478 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
481 bool JSArray::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
484 unsigned i
= propertyName
.toArrayIndex(isArrayIndex
);
486 return deleteProperty(exec
, i
);
488 if (propertyName
== exec
->propertyNames().length
)
491 return JSObject::deleteProperty(exec
, propertyName
);
494 bool JSArray::deleteProperty(ExecState
* exec
, unsigned i
)
498 ArrayStorage
* storage
= m_storage
;
500 if (i
< m_vectorLength
) {
501 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
507 --storage
->m_numValuesInVector
;
512 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
513 if (i
>= MIN_SPARSE_ARRAY_INDEX
) {
514 SparseArrayValueMap::iterator it
= map
->find(i
);
515 if (it
!= map
->end()) {
525 if (i
> MAX_ARRAY_INDEX
)
526 return deleteProperty(exec
, Identifier::from(exec
, i
));
531 void JSArray::getOwnPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
533 // FIXME: Filling PropertyNameArray with an identifier for every integer
534 // is incredibly inefficient for large arrays. We need a different approach,
535 // which almost certainly means a different structure for PropertyNameArray.
537 ArrayStorage
* storage
= m_storage
;
539 unsigned usedVectorLength
= min(storage
->m_length
, m_vectorLength
);
540 for (unsigned i
= 0; i
< usedVectorLength
; ++i
) {
541 if (storage
->m_vector
[i
])
542 propertyNames
.add(Identifier::from(exec
, i
));
545 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
546 SparseArrayValueMap::iterator end
= map
->end();
547 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
)
548 propertyNames
.add(Identifier::from(exec
, it
->first
));
551 if (mode
== IncludeDontEnumProperties
)
552 propertyNames
.add(exec
->propertyNames().length
);
554 JSObject::getOwnPropertyNames(exec
, propertyNames
, mode
);
557 ALWAYS_INLINE
unsigned JSArray::getNewVectorLength(unsigned desiredLength
)
559 ASSERT(desiredLength
<= MAX_STORAGE_VECTOR_LENGTH
);
561 unsigned increasedLength
;
562 unsigned maxInitLength
= min(m_storage
->m_length
, 100000U);
564 if (desiredLength
< maxInitLength
)
565 increasedLength
= maxInitLength
;
566 else if (!m_vectorLength
)
567 increasedLength
= max(desiredLength
, lastArraySize
);
569 // Mathematically equivalent to:
570 // increasedLength = (newLength * 3 + 1) / 2;
572 // increasedLength = (unsigned)ceil(newLength * 1.5));
573 // This form is not prone to internal overflow.
574 increasedLength
= desiredLength
+ (desiredLength
>> 1) + (desiredLength
& 1);
577 ASSERT(increasedLength
>= desiredLength
);
579 lastArraySize
= min(increasedLength
, FIRST_VECTOR_GROW
);
581 return min(increasedLength
, MAX_STORAGE_VECTOR_LENGTH
);
584 bool JSArray::increaseVectorLength(unsigned newLength
)
586 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
587 // to the vector. Callers have to account for that, because they can do it more efficiently.
589 ArrayStorage
* storage
= m_storage
;
591 unsigned vectorLength
= m_vectorLength
;
592 ASSERT(newLength
> vectorLength
);
593 ASSERT(newLength
<= MAX_STORAGE_VECTOR_INDEX
);
594 unsigned newVectorLength
= getNewVectorLength(newLength
);
595 void* baseStorage
= storage
->m_allocBase
;
597 if (!tryFastRealloc(baseStorage
, storageSize(newVectorLength
+ m_indexBias
)).getValue(baseStorage
))
600 storage
= m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(static_cast<char*>(baseStorage
) + m_indexBias
* sizeof(JSValue
));
601 m_storage
->m_allocBase
= baseStorage
;
603 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
604 for (unsigned i
= vectorLength
; i
< newVectorLength
; ++i
)
607 m_vectorLength
= newVectorLength
;
609 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
614 bool JSArray::increaseVectorPrefixLength(unsigned newLength
)
616 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
617 // to the vector. Callers have to account for that, because they can do it more efficiently.
619 ArrayStorage
* storage
= m_storage
;
621 unsigned vectorLength
= m_vectorLength
;
622 ASSERT(newLength
> vectorLength
);
623 ASSERT(newLength
<= MAX_STORAGE_VECTOR_INDEX
);
624 unsigned newVectorLength
= getNewVectorLength(newLength
);
626 void* newBaseStorage
= fastMalloc(storageSize(newVectorLength
+ m_indexBias
));
630 m_indexBias
+= newVectorLength
- newLength
;
632 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(static_cast<char*>(newBaseStorage
) + m_indexBias
* sizeof(JSValue
));
634 memcpy(m_storage
, storage
, storageSize(0));
635 memcpy(&m_storage
->m_vector
[newLength
- m_vectorLength
], &storage
->m_vector
[0], vectorLength
* sizeof(JSValue
));
637 m_storage
->m_allocBase
= newBaseStorage
;
638 m_vectorLength
= newLength
;
640 fastFree(storage
->m_allocBase
);
641 ASSERT(newLength
> vectorLength
);
642 unsigned delta
= newLength
- vectorLength
;
643 for (unsigned i
= 0; i
< delta
; i
++)
644 m_storage
->m_vector
[i
].clear();
645 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength
) - storageSize(vectorLength
));
651 void JSArray::setLength(unsigned newLength
)
653 ArrayStorage
* storage
= m_storage
;
655 #if CHECK_ARRAY_CONSISTENCY
656 if (!storage
->m_inCompactInitialization
)
659 storage
->m_inCompactInitialization
= false;
662 unsigned length
= storage
->m_length
;
664 if (newLength
< length
) {
665 unsigned usedVectorLength
= min(length
, m_vectorLength
);
666 for (unsigned i
= newLength
; i
< usedVectorLength
; ++i
) {
667 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[i
];
668 bool hadValue
= valueSlot
;
670 storage
->m_numValuesInVector
-= hadValue
;
673 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
674 SparseArrayValueMap copy
= *map
;
675 SparseArrayValueMap::iterator end
= copy
.end();
676 for (SparseArrayValueMap::iterator it
= copy
.begin(); it
!= end
; ++it
) {
677 if (it
->first
>= newLength
)
678 map
->remove(it
->first
);
680 if (map
->isEmpty()) {
682 storage
->m_sparseValueMap
= 0;
687 storage
->m_length
= newLength
;
692 JSValue
JSArray::pop()
696 ArrayStorage
* storage
= m_storage
;
698 unsigned length
= storage
->m_length
;
700 return jsUndefined();
706 if (length
< m_vectorLength
) {
707 WriteBarrier
<Unknown
>& valueSlot
= storage
->m_vector
[length
];
709 --storage
->m_numValuesInVector
;
710 result
= valueSlot
.get();
713 result
= jsUndefined();
715 result
= jsUndefined();
716 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
717 SparseArrayValueMap::iterator it
= map
->find(length
);
718 if (it
!= map
->end()) {
719 result
= it
->second
.get();
721 if (map
->isEmpty()) {
723 storage
->m_sparseValueMap
= 0;
729 storage
->m_length
= length
;
736 void JSArray::push(ExecState
* exec
, JSValue value
)
740 ArrayStorage
* storage
= m_storage
;
742 if (storage
->m_length
< m_vectorLength
) {
743 storage
->m_vector
[storage
->m_length
].set(exec
->globalData(), this, value
);
744 ++storage
->m_numValuesInVector
;
750 if (storage
->m_length
< MIN_SPARSE_ARRAY_INDEX
) {
751 SparseArrayValueMap
* map
= storage
->m_sparseValueMap
;
752 if (!map
|| map
->isEmpty()) {
753 if (increaseVectorLength(storage
->m_length
+ 1)) {
755 storage
->m_vector
[storage
->m_length
].set(exec
->globalData(), this, value
);
756 ++storage
->m_numValuesInVector
;
762 throwOutOfMemoryError(exec
);
767 putSlowCase(exec
, storage
->m_length
++, value
);
770 void JSArray::shiftCount(ExecState
* exec
, int count
)
774 ArrayStorage
* storage
= m_storage
;
776 unsigned oldLength
= storage
->m_length
;
781 if (oldLength
!= storage
->m_numValuesInVector
) {
782 // If m_length and m_numValuesInVector aren't the same, we have a sparse vector
783 // which means we need to go through each entry looking for the the "empty"
784 // slots and then fill them with possible properties. See ECMA spec.
785 // 15.4.4.9 steps 11 through 13.
786 for (unsigned i
= count
; i
< oldLength
; ++i
) {
787 if ((i
>= m_vectorLength
) || (!m_storage
->m_vector
[i
])) {
788 PropertySlot
slot(this);
789 JSValue p
= prototype();
790 if ((!p
.isNull()) && (asObject(p
)->getPropertySlot(exec
, i
, slot
)))
791 put(exec
, i
, slot
.getValue(exec
, i
));
795 storage
= m_storage
; // The put() above could have grown the vector and realloc'ed storage.
797 // Need to decrement numValuesInvector based on number of real entries
798 for (unsigned i
= 0; i
< (unsigned)count
; ++i
)
799 if ((i
< m_vectorLength
) && (storage
->m_vector
[i
]))
800 --storage
->m_numValuesInVector
;
802 storage
->m_numValuesInVector
-= count
;
804 storage
->m_length
-= count
;
806 if (m_vectorLength
) {
807 count
= min(m_vectorLength
, (unsigned)count
);
809 m_vectorLength
-= count
;
811 if (m_vectorLength
) {
812 char* newBaseStorage
= reinterpret_cast<char*>(storage
) + count
* sizeof(JSValue
);
813 memmove(newBaseStorage
, storage
, storageSize(0));
814 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(newBaseStorage
);
816 m_indexBias
+= count
;
821 void JSArray::unshiftCount(ExecState
* exec
, int count
)
823 ArrayStorage
* storage
= m_storage
;
825 ASSERT(m_indexBias
>= 0);
828 unsigned length
= storage
->m_length
;
830 if (length
!= storage
->m_numValuesInVector
) {
831 // If m_length and m_numValuesInVector aren't the same, we have a sparse vector
832 // which means we need to go through each entry looking for the the "empty"
833 // slots and then fill them with possible properties. See ECMA spec.
834 // 15.4.4.13 steps 8 through 10.
835 for (unsigned i
= 0; i
< length
; ++i
) {
836 if ((i
>= m_vectorLength
) || (!m_storage
->m_vector
[i
])) {
837 PropertySlot
slot(this);
838 JSValue p
= prototype();
839 if ((!p
.isNull()) && (asObject(p
)->getPropertySlot(exec
, i
, slot
)))
840 put(exec
, i
, slot
.getValue(exec
, i
));
845 storage
= m_storage
; // The put() above could have grown the vector and realloc'ed storage.
847 if (m_indexBias
>= count
) {
848 m_indexBias
-= count
;
849 char* newBaseStorage
= reinterpret_cast<char*>(storage
) - count
* sizeof(JSValue
);
850 memmove(newBaseStorage
, storage
, storageSize(0));
851 m_storage
= reinterpret_cast_ptr
<ArrayStorage
*>(newBaseStorage
);
852 m_vectorLength
+= count
;
853 } else if (!increaseVectorPrefixLength(m_vectorLength
+ count
)) {
854 throwOutOfMemoryError(exec
);
858 WriteBarrier
<Unknown
>* vector
= m_storage
->m_vector
;
859 for (int i
= 0; i
< count
; i
++)
863 void JSArray::visitChildren(SlotVisitor
& visitor
)
865 ASSERT_GC_OBJECT_INHERITS(this, &s_info
);
866 COMPILE_ASSERT(StructureFlags
& OverridesVisitChildren
, OverridesVisitChildrenWithoutSettingFlag
);
867 ASSERT(structure()->typeInfo().overridesVisitChildren());
868 visitChildrenDirect(visitor
);
871 static int compareNumbersForQSort(const void* a
, const void* b
)
873 double da
= static_cast<const JSValue
*>(a
)->uncheckedGetNumber();
874 double db
= static_cast<const JSValue
*>(b
)->uncheckedGetNumber();
875 return (da
> db
) - (da
< db
);
878 static int compareByStringPairForQSort(const void* a
, const void* b
)
880 const ValueStringPair
* va
= static_cast<const ValueStringPair
*>(a
);
881 const ValueStringPair
* vb
= static_cast<const ValueStringPair
*>(b
);
882 return codePointCompare(va
->second
, vb
->second
);
885 void JSArray::sortNumeric(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
887 ArrayStorage
* storage
= m_storage
;
889 unsigned lengthNotIncludingUndefined
= compactForSorting();
890 if (storage
->m_sparseValueMap
) {
891 throwOutOfMemoryError(exec
);
895 if (!lengthNotIncludingUndefined
)
898 bool allValuesAreNumbers
= true;
899 size_t size
= storage
->m_numValuesInVector
;
900 for (size_t i
= 0; i
< size
; ++i
) {
901 if (!storage
->m_vector
[i
].isNumber()) {
902 allValuesAreNumbers
= false;
907 if (!allValuesAreNumbers
)
908 return sort(exec
, compareFunction
, callType
, callData
);
910 // For numeric comparison, which is fast, qsort is faster than mergesort. We
911 // also don't require mergesort's stability, since there's no user visible
912 // side-effect from swapping the order of equal primitive values.
913 qsort(storage
->m_vector
, size
, sizeof(JSValue
), compareNumbersForQSort
);
915 checkConsistency(SortConsistencyCheck
);
918 void JSArray::sort(ExecState
* exec
)
920 ArrayStorage
* storage
= m_storage
;
922 unsigned lengthNotIncludingUndefined
= compactForSorting();
923 if (storage
->m_sparseValueMap
) {
924 throwOutOfMemoryError(exec
);
928 if (!lengthNotIncludingUndefined
)
931 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
932 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
933 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
934 // random or otherwise changing results, effectively making compare function inconsistent.
936 Vector
<ValueStringPair
> values(lengthNotIncludingUndefined
);
937 if (!values
.begin()) {
938 throwOutOfMemoryError(exec
);
942 Heap::heap(this)->pushTempSortVector(&values
);
944 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++) {
945 JSValue value
= storage
->m_vector
[i
].get();
946 ASSERT(!value
.isUndefined());
947 values
[i
].first
= value
;
950 // FIXME: The following loop continues to call toString on subsequent values even after
951 // a toString call raises an exception.
953 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
954 values
[i
].second
= values
[i
].first
.toString(exec
);
956 if (exec
->hadException()) {
957 Heap::heap(this)->popTempSortVector(&values
);
961 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
965 mergesort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
967 // FIXME: The qsort library function is likely to not be a stable sort.
968 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
969 qsort(values
.begin(), values
.size(), sizeof(ValueStringPair
), compareByStringPairForQSort
);
972 // If the toString function changed the length of the array or vector storage,
973 // increase the length to handle the orignal number of actual values.
974 if (m_vectorLength
< lengthNotIncludingUndefined
)
975 increaseVectorLength(lengthNotIncludingUndefined
);
976 if (storage
->m_length
< lengthNotIncludingUndefined
)
977 storage
->m_length
= lengthNotIncludingUndefined
;
979 JSGlobalData
& globalData
= exec
->globalData();
980 for (size_t i
= 0; i
< lengthNotIncludingUndefined
; i
++)
981 storage
->m_vector
[i
].set(globalData
, this, values
[i
].first
);
983 Heap::heap(this)->popTempSortVector(&values
);
985 checkConsistency(SortConsistencyCheck
);
988 struct AVLTreeNodeForArrayCompare
{
991 // Child pointers. The high bit of gt is robbed and used as the
992 // balance factor sign. The high bit of lt is robbed and used as
993 // the magnitude of the balance factor.
998 struct AVLTreeAbstractorForArrayCompare
{
999 typedef int32_t handle
; // Handle is an index into m_nodes vector.
1000 typedef JSValue key
;
1001 typedef int32_t size
;
1003 Vector
<AVLTreeNodeForArrayCompare
> m_nodes
;
1005 JSValue m_compareFunction
;
1006 CallType m_compareCallType
;
1007 const CallData
* m_compareCallData
;
1008 JSValue m_globalThisValue
;
1009 OwnPtr
<CachedCall
> m_cachedCall
;
1011 handle
get_less(handle h
) { return m_nodes
[h
].lt
& 0x7FFFFFFF; }
1012 void set_less(handle h
, handle lh
) { m_nodes
[h
].lt
&= 0x80000000; m_nodes
[h
].lt
|= lh
; }
1013 handle
get_greater(handle h
) { return m_nodes
[h
].gt
& 0x7FFFFFFF; }
1014 void set_greater(handle h
, handle gh
) { m_nodes
[h
].gt
&= 0x80000000; m_nodes
[h
].gt
|= gh
; }
1016 int get_balance_factor(handle h
)
1018 if (m_nodes
[h
].gt
& 0x80000000)
1020 return static_cast<unsigned>(m_nodes
[h
].lt
) >> 31;
1023 void set_balance_factor(handle h
, int bf
)
1026 m_nodes
[h
].lt
&= 0x7FFFFFFF;
1027 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1029 m_nodes
[h
].lt
|= 0x80000000;
1031 m_nodes
[h
].gt
|= 0x80000000;
1033 m_nodes
[h
].gt
&= 0x7FFFFFFF;
1037 int compare_key_key(key va
, key vb
)
1039 ASSERT(!va
.isUndefined());
1040 ASSERT(!vb
.isUndefined());
1042 if (m_exec
->hadException())
1045 double compareResult
;
1047 m_cachedCall
->setThis(m_globalThisValue
);
1048 m_cachedCall
->setArgument(0, va
);
1049 m_cachedCall
->setArgument(1, vb
);
1050 compareResult
= m_cachedCall
->call().toNumber(m_cachedCall
->newCallFrame(m_exec
));
1052 MarkedArgumentBuffer arguments
;
1053 arguments
.append(va
);
1054 arguments
.append(vb
);
1055 compareResult
= call(m_exec
, m_compareFunction
, m_compareCallType
, *m_compareCallData
, m_globalThisValue
, arguments
).toNumber(m_exec
);
1057 return (compareResult
< 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1060 int compare_key_node(key k
, handle h
) { return compare_key_key(k
, m_nodes
[h
].value
); }
1061 int compare_node_node(handle h1
, handle h2
) { return compare_key_key(m_nodes
[h1
].value
, m_nodes
[h2
].value
); }
1063 static handle
null() { return 0x7FFFFFFF; }
1066 void JSArray::sort(ExecState
* exec
, JSValue compareFunction
, CallType callType
, const CallData
& callData
)
1070 ArrayStorage
* storage
= m_storage
;
1072 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1074 // The maximum tree depth is compiled in - but the caller is clearly up to no good
1075 // if a larger array is passed.
1076 ASSERT(storage
->m_length
<= static_cast<unsigned>(std::numeric_limits
<int>::max()));
1077 if (storage
->m_length
> static_cast<unsigned>(std::numeric_limits
<int>::max()))
1080 unsigned usedVectorLength
= min(storage
->m_length
, m_vectorLength
);
1081 unsigned nodeCount
= usedVectorLength
+ (storage
->m_sparseValueMap
? storage
->m_sparseValueMap
->size() : 0);
1086 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44> tree
; // Depth 44 is enough for 2^31 items
1087 tree
.abstractor().m_exec
= exec
;
1088 tree
.abstractor().m_compareFunction
= compareFunction
;
1089 tree
.abstractor().m_compareCallType
= callType
;
1090 tree
.abstractor().m_compareCallData
= &callData
;
1091 tree
.abstractor().m_globalThisValue
= exec
->globalThisValue();
1092 tree
.abstractor().m_nodes
.grow(nodeCount
);
1094 if (callType
== CallTypeJS
)
1095 tree
.abstractor().m_cachedCall
= adoptPtr(new CachedCall(exec
, asFunction(compareFunction
), 2));
1097 if (!tree
.abstractor().m_nodes
.begin()) {
1098 throwOutOfMemoryError(exec
);
1102 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1103 // right out from under us while we're building the tree here.
1105 unsigned numDefined
= 0;
1106 unsigned numUndefined
= 0;
1108 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1109 for (; numDefined
< usedVectorLength
; ++numDefined
) {
1110 JSValue v
= storage
->m_vector
[numDefined
].get();
1111 if (!v
|| v
.isUndefined())
1113 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1114 tree
.insert(numDefined
);
1116 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
1117 JSValue v
= storage
->m_vector
[i
].get();
1119 if (v
.isUndefined())
1122 tree
.abstractor().m_nodes
[numDefined
].value
= v
;
1123 tree
.insert(numDefined
);
1129 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1131 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
1132 newUsedVectorLength
+= map
->size();
1133 if (newUsedVectorLength
> m_vectorLength
) {
1134 // Check that it is possible to allocate an array large enough to hold all the entries.
1135 if ((newUsedVectorLength
> MAX_STORAGE_VECTOR_LENGTH
) || !increaseVectorLength(newUsedVectorLength
)) {
1136 throwOutOfMemoryError(exec
);
1141 storage
= m_storage
;
1143 SparseArrayValueMap::iterator end
= map
->end();
1144 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
) {
1145 tree
.abstractor().m_nodes
[numDefined
].value
= it
->second
.get();
1146 tree
.insert(numDefined
);
1151 storage
->m_sparseValueMap
= 0;
1154 ASSERT(tree
.abstractor().m_nodes
.size() >= numDefined
);
1156 // FIXME: If the compare function changed the length of the array, the following might be
1157 // modifying the vector incorrectly.
1159 // Copy the values back into m_storage.
1160 AVLTree
<AVLTreeAbstractorForArrayCompare
, 44>::Iterator iter
;
1161 iter
.start_iter_least(tree
);
1162 JSGlobalData
& globalData
= exec
->globalData();
1163 for (unsigned i
= 0; i
< numDefined
; ++i
) {
1164 storage
->m_vector
[i
].set(globalData
, this, tree
.abstractor().m_nodes
[*iter
].value
);
1168 // Put undefined values back in.
1169 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
1170 storage
->m_vector
[i
].setUndefined();
1172 // Ensure that unused values in the vector are zeroed out.
1173 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
1174 storage
->m_vector
[i
].clear();
1176 storage
->m_numValuesInVector
= newUsedVectorLength
;
1178 checkConsistency(SortConsistencyCheck
);
1181 void JSArray::fillArgList(ExecState
* exec
, MarkedArgumentBuffer
& args
)
1183 ArrayStorage
* storage
= m_storage
;
1185 WriteBarrier
<Unknown
>* vector
= storage
->m_vector
;
1186 unsigned vectorEnd
= min(storage
->m_length
, m_vectorLength
);
1188 for (; i
< vectorEnd
; ++i
) {
1189 WriteBarrier
<Unknown
>& v
= vector
[i
];
1192 args
.append(v
.get());
1195 for (; i
< storage
->m_length
; ++i
)
1196 args
.append(get(exec
, i
));
1199 void JSArray::copyToRegisters(ExecState
* exec
, Register
* buffer
, uint32_t maxSize
)
1201 ASSERT(m_storage
->m_length
>= maxSize
);
1202 UNUSED_PARAM(maxSize
);
1203 WriteBarrier
<Unknown
>* vector
= m_storage
->m_vector
;
1204 unsigned vectorEnd
= min(maxSize
, m_vectorLength
);
1206 for (; i
< vectorEnd
; ++i
) {
1207 WriteBarrier
<Unknown
>& v
= vector
[i
];
1210 buffer
[i
] = v
.get();
1213 for (; i
< maxSize
; ++i
)
1214 buffer
[i
] = get(exec
, i
);
1217 unsigned JSArray::compactForSorting()
1221 ArrayStorage
* storage
= m_storage
;
1223 unsigned usedVectorLength
= min(storage
->m_length
, m_vectorLength
);
1225 unsigned numDefined
= 0;
1226 unsigned numUndefined
= 0;
1228 for (; numDefined
< usedVectorLength
; ++numDefined
) {
1229 JSValue v
= storage
->m_vector
[numDefined
].get();
1230 if (!v
|| v
.isUndefined())
1234 for (unsigned i
= numDefined
; i
< usedVectorLength
; ++i
) {
1235 JSValue v
= storage
->m_vector
[i
].get();
1237 if (v
.isUndefined())
1240 storage
->m_vector
[numDefined
++].setWithoutWriteBarrier(v
);
1244 unsigned newUsedVectorLength
= numDefined
+ numUndefined
;
1246 if (SparseArrayValueMap
* map
= storage
->m_sparseValueMap
) {
1247 newUsedVectorLength
+= map
->size();
1248 if (newUsedVectorLength
> m_vectorLength
) {
1249 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
1250 // exception is thrown by caller.
1251 if ((newUsedVectorLength
> MAX_STORAGE_VECTOR_LENGTH
) || !increaseVectorLength(newUsedVectorLength
))
1254 storage
= m_storage
;
1257 SparseArrayValueMap::iterator end
= map
->end();
1258 for (SparseArrayValueMap::iterator it
= map
->begin(); it
!= end
; ++it
)
1259 storage
->m_vector
[numDefined
++].setWithoutWriteBarrier(it
->second
.get());
1262 storage
->m_sparseValueMap
= 0;
1265 for (unsigned i
= numDefined
; i
< newUsedVectorLength
; ++i
)
1266 storage
->m_vector
[i
].setUndefined();
1267 for (unsigned i
= newUsedVectorLength
; i
< usedVectorLength
; ++i
)
1268 storage
->m_vector
[i
].clear();
1270 storage
->m_numValuesInVector
= newUsedVectorLength
;
1272 checkConsistency(SortConsistencyCheck
);
1277 void* JSArray::subclassData() const
1279 return m_storage
->subclassData
;
1282 void JSArray::setSubclassData(void* d
)
1284 m_storage
->subclassData
= d
;
1287 #if CHECK_ARRAY_CONSISTENCY
1289 void JSArray::checkConsistency(ConsistencyCheckType type
)
1291 ArrayStorage
* storage
= m_storage
;
1294 if (type
== SortConsistencyCheck
)
1295 ASSERT(!storage
->m_sparseValueMap
);
1297 unsigned numValuesInVector
= 0;
1298 for (unsigned i
= 0; i
< m_vectorLength
; ++i
) {
1299 if (JSValue value
= storage
->m_vector
[i
]) {
1300 ASSERT(i
< storage
->m_length
);
1301 if (type
!= DestructorConsistencyCheck
)
1302 value
.isUndefined(); // Likely to crash if the object was deallocated.
1303 ++numValuesInVector
;
1305 if (type
== SortConsistencyCheck
)
1306 ASSERT(i
>= storage
->m_numValuesInVector
);
1309 ASSERT(numValuesInVector
== storage
->m_numValuesInVector
);
1310 ASSERT(numValuesInVector
<= storage
->m_length
);
1312 if (storage
->m_sparseValueMap
) {
1313 SparseArrayValueMap::iterator end
= storage
->m_sparseValueMap
->end();
1314 for (SparseArrayValueMap::iterator it
= storage
->m_sparseValueMap
->begin(); it
!= end
; ++it
) {
1315 unsigned index
= it
->first
;
1316 ASSERT(index
< storage
->m_length
);
1317 ASSERT(index
>= storage
->m_vectorLength
);
1318 ASSERT(index
<= MAX_ARRAY_INDEX
);
1320 if (type
!= DestructorConsistencyCheck
)
1321 it
->second
.isUndefined(); // Likely to crash if the object was deallocated.