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);