1/*
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)
6 *
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.
11 *
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.
16 *
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
20 *
21 */
22
23#include "config.h"
24#include "JSArray.h"
25
26#include "ArrayPrototype.h"
27#include "CachedCall.h"
28#include "Error.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>
35
36#define CHECK_ARRAY_CONSISTENCY 0
37
38using namespace std;
39using namespace WTF;
40
41namespace JSC {
42
43ASSERT_CLASS_FITS_IN_CELL(JSArray);
44
45// Overview of JSArray
46//
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.
51//
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.
56//
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
60// fashion:
61//
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.
69
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))
75
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
82
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.
87static const unsigned minDensityMultiplier = 8;
88
89const ClassInfo JSArray::info = {"Array", 0, 0, 0};
90
91static inline size_t storageSize(unsigned vectorLength)
92{
93    ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
94
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))));
101
102    return size;
103}
104
105static inline unsigned increasedVectorLength(unsigned newLength)
106{
107    ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH);
108
109    // Mathematically equivalent to:
110    //   increasedLength = (newLength * 3 + 1) / 2;
111    // or:
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);
116
117    return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
118}
119
120static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
121{
122    return length / minDensityMultiplier <= numValues;
123}
124
125#if !CHECK_ARRAY_CONSISTENCY
126
127inline void JSArray::checkConsistency(ConsistencyCheckType)
128{
129}
130
131#endif
132
133JSArray::JSArray(NonNullPassRefPtr<Structure> structure)
134    : JSObject(structure)
135{
136    unsigned initialCapacity = 0;
137
138    m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
139    m_vectorLength = initialCapacity;
140
141    checkConsistency();
142}
143
144JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength)
145    : JSObject(structure)
146{
147    unsigned initialCapacity = min(initialLength, MIN_SPARSE_ARRAY_INDEX);
148
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->lazyCreationData = 0;
155    m_storage->reportedMapCapacity = 0;
156
157    JSValue* vector = m_storage->m_vector;
158    for (size_t i = 0; i < initialCapacity; ++i)
159        vector[i] = JSValue();
160
161    checkConsistency();
162
163    Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue));
164}
165
166JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list)
167    : JSObject(structure)
168{
169    unsigned initialCapacity = list.size();
170
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->lazyCreationData = 0;
177    m_storage->reportedMapCapacity = 0;
178
179    size_t i = 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;
183
184    checkConsistency();
185
186    Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity));
187}
188
189JSArray::~JSArray()
190{
191    ASSERT(vptr() == JSGlobalData::jsArrayVPtr);
192    checkConsistency(DestructorConsistencyCheck);
193
194    delete m_storage->m_sparseValueMap;
195    fastFree(m_storage);
196}
197
198bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
199{
200    ArrayStorage* storage = m_storage;
201
202    if (i >= storage->m_length) {
203        if (i > MAX_ARRAY_INDEX)
204            return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
205        return false;
206    }
207
208    if (i < m_vectorLength) {
209        JSValue& valueSlot = storage->m_vector[i];
210        if (valueSlot) {
211            slot.setValueSlot(&valueSlot);
212            return true;
213        }
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);
219                return true;
220            }
221        }
222    }
223
224    return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
225}
226
227bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
228{
229    if (propertyName == exec->propertyNames().length) {
230        slot.setValue(jsNumber(exec, length()));
231        return true;
232    }
233
234    bool isArrayIndex;
235    unsigned i = propertyName.toArrayIndex(&isArrayIndex);
236    if (isArrayIndex)
237        return JSArray::getOwnPropertySlot(exec, i, slot);
238
239    return JSObject::getOwnPropertySlot(exec, propertyName, slot);
240}
241
242bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
243{
244    if (propertyName == exec->propertyNames().length) {
245        descriptor.setDescriptor(jsNumber(exec, length()), DontDelete | DontEnum);
246        return true;
247    }
248
249    bool isArrayIndex;
250    unsigned i = propertyName.toArrayIndex(&isArrayIndex);
251    if (isArrayIndex) {
252        if (i >= m_storage->m_length)
253            return false;
254        if (i < m_vectorLength) {
255            JSValue& value = m_storage->m_vector[i];
256            if (value) {
257                descriptor.setDescriptor(value, 0);
258                return true;
259            }
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);
265                    return true;
266                }
267            }
268        }
269    }
270    return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
271}
272
273// ECMA 15.4.5.1
274void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
275{
276    bool isArrayIndex;
277    unsigned i = propertyName.toArrayIndex(&isArrayIndex);
278    if (isArrayIndex) {
279        put(exec, i, value);
280        return;
281    }
282
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.");
287            return;
288        }
289        setLength(newLength);
290        return;
291    }
292
293    JSObject::put(exec, propertyName, value, slot);
294}
295
296void JSArray::put(ExecState* exec, unsigned i, JSValue value)
297{
298    checkConsistency();
299
300    unsigned length = m_storage->m_length;
301    if (i >= length && i <= MAX_ARRAY_INDEX) {
302        length = i + 1;
303        m_storage->m_length = length;
304    }
305
306    if (i < m_vectorLength) {
307        JSValue& valueSlot = m_storage->m_vector[i];
308        if (valueSlot) {
309            valueSlot = value;
310            checkConsistency();
311            return;
312        }
313        valueSlot = value;
314        ++m_storage->m_numValuesInVector;
315        checkConsistency();
316        return;
317    }
318
319    putSlowCase(exec, i, value);
320}
321
322NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value)
323{
324    ArrayStorage* storage = m_storage;
325    SparseArrayValueMap* map = storage->m_sparseValueMap;
326
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);
331            return;
332        }
333
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)) {
337            if (!map) {
338                map = new SparseArrayValueMap;
339                storage->m_sparseValueMap = map;
340            }
341
342            pair<SparseArrayValueMap::iterator, bool> result = map->add(i, value);
343            if (!result.second) { // pre-existing entry
344                result.first->second = value;
345                return;
346            }
347
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;
352            }
353            return;
354        }
355    }
356
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)) {
361            storage = m_storage;
362            storage->m_vector[i] = value;
363            ++storage->m_numValuesInVector;
364            checkConsistency();
365        } else
366            throwOutOfMemoryError(exec);
367        return;
368    }
369
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))
385                break;
386            newVectorLength = proposedNewVectorLength;
387            newNumValuesInVector = proposedNewNumValuesInVector;
388        }
389    }
390
391    if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) {
392        throwOutOfMemoryError(exec);
393        return;
394    }
395
396    unsigned vectorLength = m_vectorLength;
397
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)
402            map->remove(i);
403    } else {
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);
408    }
409
410    storage->m_vector[i] = value;
411
412    m_vectorLength = newVectorLength;
413    storage->m_numValuesInVector = newNumValuesInVector;
414
415    m_storage = storage;
416
417    checkConsistency();
418
419    Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
420}
421
422bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName)
423{
424    bool isArrayIndex;
425    unsigned i = propertyName.toArrayIndex(&isArrayIndex);
426    if (isArrayIndex)
427        return deleteProperty(exec, i);
428
429    if (propertyName == exec->propertyNames().length)
430        return false;
431
432    return JSObject::deleteProperty(exec, propertyName);
433}
434
435bool JSArray::deleteProperty(ExecState* exec, unsigned i)
436{
437    checkConsistency();
438
439    ArrayStorage* storage = m_storage;
440
441    if (i < m_vectorLength) {
442        JSValue& valueSlot = storage->m_vector[i];
443        if (!valueSlot) {
444            checkConsistency();
445            return false;
446        }
447        valueSlot = JSValue();
448        --storage->m_numValuesInVector;
449        checkConsistency();
450        return true;
451    }
452
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()) {
457                map->remove(it);
458                checkConsistency();
459                return true;
460            }
461        }
462    }
463
464    checkConsistency();
465
466    if (i > MAX_ARRAY_INDEX)
467        return deleteProperty(exec, Identifier::from(exec, i));
468
469    return false;
470}
471
472void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
473{
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.
477
478    ArrayStorage* storage = m_storage;
479
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));
484    }
485
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));
490    }
491
492    if (mode == IncludeDontEnumProperties)
493        propertyNames.add(exec->propertyNames().length);
494
495    JSObject::getOwnPropertyNames(exec, propertyNames, mode);
496}
497
498bool JSArray::increaseVectorLength(unsigned newLength)
499{
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.
502
503    ArrayStorage* storage = m_storage;
504
505    unsigned vectorLength = m_vectorLength;
506    ASSERT(newLength > vectorLength);
507    ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
508    unsigned newVectorLength = increasedVectorLength(newLength);
509
510    if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage))
511        return false;
512
513    m_vectorLength = newVectorLength;
514
515    for (unsigned i = vectorLength; i < newVectorLength; ++i)
516        storage->m_vector[i] = JSValue();
517
518    m_storage = storage;
519
520    Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
521
522    return true;
523}
524
525void JSArray::setLength(unsigned newLength)
526{
527    checkConsistency();
528
529    ArrayStorage* storage = m_storage;
530
531    unsigned length = m_storage->m_length;
532
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;
540        }
541
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);
548            }
549            if (map->isEmpty()) {
550                delete map;
551                storage->m_sparseValueMap = 0;
552            }
553        }
554    }
555
556    m_storage->m_length = newLength;
557
558    checkConsistency();
559}
560
561JSValue JSArray::pop()
562{
563    checkConsistency();
564
565    unsigned length = m_storage->m_length;
566    if (!length)
567        return jsUndefined();
568
569    --length;
570
571    JSValue result;
572
573    if (length < m_vectorLength) {
574        JSValue& valueSlot = m_storage->m_vector[length];
575        if (valueSlot) {
576            --m_storage->m_numValuesInVector;
577            result = valueSlot;
578            valueSlot = JSValue();
579        } else
580            result = jsUndefined();
581    } else {
582        result = jsUndefined();
583        if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
584            SparseArrayValueMap::iterator it = map->find(length);
585            if (it != map->end()) {
586                result = it->second;
587                map->remove(it);
588                if (map->isEmpty()) {
589                    delete map;
590                    m_storage->m_sparseValueMap = 0;
591                }
592            }
593        }
594    }
595
596    m_storage->m_length = length;
597
598    checkConsistency();
599
600    return result;
601}
602
603void JSArray::push(ExecState* exec, JSValue value)
604{
605    checkConsistency();
606
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;
611        checkConsistency();
612        return;
613    }
614
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;
622                checkConsistency();
623                return;
624            }
625            checkConsistency();
626            throwOutOfMemoryError(exec);
627            return;
628        }
629    }
630
631    putSlowCase(exec, m_storage->m_length++, value);
632}
633
634void JSArray::markChildren(MarkStack& markStack)
635{
636    markChildrenDirect(markStack);
637}
638
639static int compareNumbersForQSort(const void* a, const void* b)
640{
641    double da = static_cast<const JSValue*>(a)->uncheckedGetNumber();
642    double db = static_cast<const JSValue*>(b)->uncheckedGetNumber();
643    return (da > db) - (da < db);
644}
645
646typedef std::pair<JSValue, UString> ValueStringPair;
647
648static int compareByStringPairForQSort(const void* a, const void* b)
649{
650    const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
651    const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
652    return compare(va->second, vb->second);
653}
654
655void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
656{
657    unsigned lengthNotIncludingUndefined = compactForSorting();
658    if (m_storage->m_sparseValueMap) {
659        throwOutOfMemoryError(exec);
660        return;
661    }
662
663    if (!lengthNotIncludingUndefined)
664        return;
665
666    bool allValuesAreNumbers = true;
667    size_t size = m_storage->m_numValuesInVector;
668    for (size_t i = 0; i < size; ++i) {
669        if (!m_storage->m_vector[i].isNumber()) {
670            allValuesAreNumbers = false;
671            break;
672        }
673    }
674
675    if (!allValuesAreNumbers)
676        return sort(exec, compareFunction, callType, callData);
677
678    // For numeric comparison, which is fast, qsort is faster than mergesort. We
679    // also don't require mergesort's stability, since there's no user visible
680    // side-effect from swapping the order of equal primitive values.
681    qsort(m_storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort);
682
683    checkConsistency(SortConsistencyCheck);
684}
685
686void JSArray::sort(ExecState* exec)
687{
688    unsigned lengthNotIncludingUndefined = compactForSorting();
689    if (m_storage->m_sparseValueMap) {
690        throwOutOfMemoryError(exec);
691        return;
692    }
693
694    if (!lengthNotIncludingUndefined)
695        return;
696
697    // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
698    // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
699    // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
700    // random or otherwise changing results, effectively making compare function inconsistent.
701
702    Vector<ValueStringPair> values(lengthNotIncludingUndefined);
703    if (!values.begin()) {
704        throwOutOfMemoryError(exec);
705        return;
706    }
707
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;
712    }
713
714    // FIXME: While calling these toString functions, the array could be mutated.
715    // In that case, objects pointed to by values in this vector might get garbage-collected!
716
717    // FIXME: The following loop continues to call toString on subsequent values even after
718    // a toString call raises an exception.
719
720    for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
721        values[i].second = values[i].first.toString(exec);
722
723    if (exec->hadException())
724        return;
725
726    // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
727    // than O(N log N).
728
729#if HAVE(MERGESORT)
730    mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
731#else
732    // FIXME: The qsort library function is likely to not be a stable sort.
733    // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
734    qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
735#endif
736
737    // FIXME: If the toString function changed the length of the array, this might be
738    // modifying the vector incorrectly.
739
740    for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
741        m_storage->m_vector[i] = values[i].first;
742
743    checkConsistency(SortConsistencyCheck);
744}
745
746struct AVLTreeNodeForArrayCompare {
747    JSValue value;
748
749    // Child pointers.  The high bit of gt is robbed and used as the
750    // balance factor sign.  The high bit of lt is robbed and used as
751    // the magnitude of the balance factor.
752    int32_t gt;
753    int32_t lt;
754};
755
756struct AVLTreeAbstractorForArrayCompare {
757    typedef int32_t handle; // Handle is an index into m_nodes vector.
758    typedef JSValue key;
759    typedef int32_t size;
760
761    Vector<AVLTreeNodeForArrayCompare> m_nodes;
762    ExecState* m_exec;
763    JSValue m_compareFunction;
764    CallType m_compareCallType;
765    const CallData* m_compareCallData;
766    JSValue m_globalThisValue;
767    OwnPtr<CachedCall> m_cachedCall;
768
769    handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
770    void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
771    handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
772    void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
773
774    int get_balance_factor(handle h)
775    {
776        if (m_nodes[h].gt & 0x80000000)
777            return -1;
778        return static_cast<unsigned>(m_nodes[h].lt) >> 31;
779    }
780
781    void set_balance_factor(handle h, int bf)
782    {
783        if (bf == 0) {
784            m_nodes[h].lt &= 0x7FFFFFFF;
785            m_nodes[h].gt &= 0x7FFFFFFF;
786        } else {
787            m_nodes[h].lt |= 0x80000000;
788            if (bf < 0)
789                m_nodes[h].gt |= 0x80000000;
790            else
791                m_nodes[h].gt &= 0x7FFFFFFF;
792        }
793    }
794
795    int compare_key_key(key va, key vb)
796    {
797        ASSERT(!va.isUndefined());
798        ASSERT(!vb.isUndefined());
799
800        if (m_exec->hadException())
801            return 1;
802
803        double compareResult;
804        if (m_cachedCall) {
805            m_cachedCall->setThis(m_globalThisValue);
806            m_cachedCall->setArgument(0, va);
807            m_cachedCall->setArgument(1, vb);
808            compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec));
809        } else {
810            MarkedArgumentBuffer arguments;
811            arguments.append(va);
812            arguments.append(vb);
813            compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments).toNumber(m_exec);
814        }
815        return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
816    }
817
818    int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
819    int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
820
821    static handle null() { return 0x7FFFFFFF; }
822};
823
824void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
825{
826    checkConsistency();
827
828    // FIXME: This ignores exceptions raised in the compare function or in toNumber.
829
830    // The maximum tree depth is compiled in - but the caller is clearly up to no good
831    // if a larger array is passed.
832    ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
833    if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
834        return;
835
836    if (!m_storage->m_length)
837        return;
838
839    unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
840
841    AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
842    tree.abstractor().m_exec = exec;
843    tree.abstractor().m_compareFunction = compareFunction;
844    tree.abstractor().m_compareCallType = callType;
845    tree.abstractor().m_compareCallData = &callData;
846    tree.abstractor().m_globalThisValue = exec->globalThisValue();
847    tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0));
848
849    if (callType == CallTypeJS)
850        tree.abstractor().m_cachedCall.set(new CachedCall(exec, asFunction(compareFunction), 2, exec->exceptionSlot()));
851
852    if (!tree.abstractor().m_nodes.begin()) {
853        throwOutOfMemoryError(exec);
854        return;
855    }
856
857    // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
858    // right out from under us while we're building the tree here.
859
860    unsigned numDefined = 0;
861    unsigned numUndefined = 0;
862
863    // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
864    for (; numDefined < usedVectorLength; ++numDefined) {
865        JSValue v = m_storage->m_vector[numDefined];
866        if (!v || v.isUndefined())
867            break;
868        tree.abstractor().m_nodes[numDefined].value = v;
869        tree.insert(numDefined);
870    }
871    for (unsigned i = numDefined; i < usedVectorLength; ++i) {
872        JSValue v = m_storage->m_vector[i];
873        if (v) {
874            if (v.isUndefined())
875                ++numUndefined;
876            else {
877                tree.abstractor().m_nodes[numDefined].value = v;
878                tree.insert(numDefined);
879                ++numDefined;
880            }
881        }
882    }
883
884    unsigned newUsedVectorLength = numDefined + numUndefined;
885
886    if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
887        newUsedVectorLength += map->size();
888        if (newUsedVectorLength > m_vectorLength) {
889            // Check that it is possible to allocate an array large enough to hold all the entries.
890            if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) {
891                throwOutOfMemoryError(exec);
892                return;
893            }
894        }
895
896        SparseArrayValueMap::iterator end = map->end();
897        for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
898            tree.abstractor().m_nodes[numDefined].value = it->second;
899            tree.insert(numDefined);
900            ++numDefined;
901        }
902
903        delete map;
904        m_storage->m_sparseValueMap = 0;
905    }
906
907    ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
908
909    // FIXME: If the compare function changed the length of the array, the following might be
910    // modifying the vector incorrectly.
911
912    // Copy the values back into m_storage.
913    AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
914    iter.start_iter_least(tree);
915    for (unsigned i = 0; i < numDefined; ++i) {
916        m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value;
917        ++iter;
918    }
919
920    // Put undefined values back in.
921    for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
922        m_storage->m_vector[i] = jsUndefined();
923
924    // Ensure that unused values in the vector are zeroed out.
925    for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
926        m_storage->m_vector[i] = JSValue();
927
928    m_storage->m_numValuesInVector = newUsedVectorLength;
929
930    checkConsistency(SortConsistencyCheck);
931}
932
933void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
934{
935    JSValue* vector = m_storage->m_vector;
936    unsigned vectorEnd = min(m_storage->m_length, m_vectorLength);
937    unsigned i = 0;
938    for (; i < vectorEnd; ++i) {
939        JSValue& v = vector[i];
940        if (!v)
941            break;
942        args.append(v);
943    }
944
945    for (; i < m_storage->m_length; ++i)
946        args.append(get(exec, i));
947}
948
949void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize)
950{
951    ASSERT(m_storage->m_length == maxSize);
952    UNUSED_PARAM(maxSize);
953    JSValue* vector = m_storage->m_vector;
954    unsigned vectorEnd = min(m_storage->m_length, m_vectorLength);
955    unsigned i = 0;
956    for (; i < vectorEnd; ++i) {
957        JSValue& v = vector[i];
958        if (!v)
959            break;
960        buffer[i] = v;
961    }
962
963    for (; i < m_storage->m_length; ++i)
964        buffer[i] = get(exec, i);
965}
966
967unsigned JSArray::compactForSorting()
968{
969    checkConsistency();
970
971    ArrayStorage* storage = m_storage;
972
973    unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
974
975    unsigned numDefined = 0;
976    unsigned numUndefined = 0;
977
978    for (; numDefined < usedVectorLength; ++numDefined) {
979        JSValue v = storage->m_vector[numDefined];
980        if (!v || v.isUndefined())
981            break;
982    }
983    for (unsigned i = numDefined; i < usedVectorLength; ++i) {
984        JSValue v = storage->m_vector[i];
985        if (v) {
986            if (v.isUndefined())
987                ++numUndefined;
988            else
989                storage->m_vector[numDefined++] = v;
990        }
991    }
992
993    unsigned newUsedVectorLength = numDefined + numUndefined;
994
995    if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
996        newUsedVectorLength += map->size();
997        if (newUsedVectorLength > m_vectorLength) {
998            // Check that it is possible to allocate an array large enough to hold all the entries - if not,
999            // exception is thrown by caller.
1000            if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength))
1001                return 0;
1002            storage = m_storage;
1003        }
1004
1005        SparseArrayValueMap::iterator end = map->end();
1006        for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
1007            storage->m_vector[numDefined++] = it->second;
1008
1009        delete map;
1010        storage->m_sparseValueMap = 0;
1011    }
1012
1013    for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
1014        storage->m_vector[i] = jsUndefined();
1015    for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
1016        storage->m_vector[i] = JSValue();
1017
1018    storage->m_numValuesInVector = newUsedVectorLength;
1019
1020    checkConsistency(SortConsistencyCheck);
1021
1022    return numDefined;
1023}
1024
1025void* JSArray::lazyCreationData()
1026{
1027    return m_storage->lazyCreationData;
1028}
1029
1030void JSArray::setLazyCreationData(void* d)
1031{
1032    m_storage->lazyCreationData = d;
1033}
1034
1035#if CHECK_ARRAY_CONSISTENCY
1036
1037void JSArray::checkConsistency(ConsistencyCheckType type)
1038{
1039    ASSERT(m_storage);
1040    if (type == SortConsistencyCheck)
1041        ASSERT(!m_storage->m_sparseValueMap);
1042
1043    unsigned numValuesInVector = 0;
1044    for (unsigned i = 0; i < m_vectorLength; ++i) {
1045        if (JSValue value = m_storage->m_vector[i]) {
1046            ASSERT(i < m_storage->m_length);
1047            if (type != DestructorConsistencyCheck)
1048                value->type(); // Likely to crash if the object was deallocated.
1049            ++numValuesInVector;
1050        } else {
1051            if (type == SortConsistencyCheck)
1052                ASSERT(i >= m_storage->m_numValuesInVector);
1053        }
1054    }
1055    ASSERT(numValuesInVector == m_storage->m_numValuesInVector);
1056    ASSERT(numValuesInVector <= m_storage->m_length);
1057
1058    if (m_storage->m_sparseValueMap) {
1059        SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end();
1060        for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) {
1061            unsigned index = it->first;
1062            ASSERT(index < m_storage->m_length);
1063            ASSERT(index >= m_vectorLength);
1064            ASSERT(index <= MAX_ARRAY_INDEX);
1065            ASSERT(it->second);
1066            if (type != DestructorConsistencyCheck)
1067                it->second->type(); // Likely to crash if the object was deallocated.
1068        }
1069    }
1070}
1071
1072#endif
1073
1074} // namespace JSC
1075