• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "PropertyNameArray.h"
29 #include <wtf/AVLTree.h>
30 #include <wtf/Assertions.h>
31 #include <wtf/OwnPtr.h>
32 #include <Operations.h>
33 
34 #define CHECK_ARRAY_CONSISTENCY 0
35 
36 using namespace std;
37 using namespace WTF;
38 
39 namespace JSC {
40 
41 ASSERT_CLASS_FITS_IN_CELL(JSArray);
42 
43 // Overview of JSArray
44 //
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.
49 //
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.
54 //
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
58 // fashion:
59 //
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.
67 
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))
73 
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
80 
81 // Our policy for when to use a vector and when to use a sparse map.
82 // For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
83 // When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
84 // as long as it is 1/8 full. If more sparse than that, we use a map.
85 static const unsigned minDensityMultiplier = 8;
86 
87 const ClassInfo JSArray::info = {"Array", 0, 0, 0};
88 
storageSize(unsigned vectorLength)89 static inline size_t storageSize(unsigned vectorLength)
90 {
91     ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
92 
93     // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
94     // - as asserted above - the following calculation cannot overflow.
95     size_t size = (sizeof(ArrayStorage) - sizeof(JSValue)) + (vectorLength * sizeof(JSValue));
96     // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
97     // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
98     ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValue))));
99 
100     return size;
101 }
102 
increasedVectorLength(unsigned newLength)103 static inline unsigned increasedVectorLength(unsigned newLength)
104 {
105     ASSERT(newLength <= MAX_STORAGE_VECTOR_LENGTH);
106 
107     // Mathematically equivalent to:
108     //   increasedLength = (newLength * 3 + 1) / 2;
109     // or:
110     //   increasedLength = (unsigned)ceil(newLength * 1.5));
111     // This form is not prone to internal overflow.
112     unsigned increasedLength = newLength + (newLength >> 1) + (newLength & 1);
113     ASSERT(increasedLength >= newLength);
114 
115     return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
116 }
117 
isDenseEnoughForVector(unsigned length,unsigned numValues)118 static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
119 {
120     return length / minDensityMultiplier <= numValues;
121 }
122 
123 #if !CHECK_ARRAY_CONSISTENCY
124 
checkConsistency(ConsistencyCheckType)125 inline void JSArray::checkConsistency(ConsistencyCheckType)
126 {
127 }
128 
129 #endif
130 
JSArray(PassRefPtr<Structure> structure)131 JSArray::JSArray(PassRefPtr<Structure> structure)
132     : JSObject(structure)
133 {
134     unsigned initialCapacity = 0;
135 
136     m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
137     m_storage->m_vectorLength = initialCapacity;
138 
139     m_fastAccessCutoff = 0;
140 
141     checkConsistency();
142 }
143 
JSArray(PassRefPtr<Structure> structure,unsigned initialLength)144 JSArray::JSArray(PassRefPtr<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_storage->m_vectorLength = initialCapacity;
152     m_storage->m_numValuesInVector = 0;
153     m_storage->m_sparseValueMap = 0;
154     m_storage->lazyCreationData = 0;
155 
156     JSValue* vector = m_storage->m_vector;
157     for (size_t i = 0; i < initialCapacity; ++i)
158         vector[i] = JSValue();
159 
160     m_fastAccessCutoff = 0;
161 
162     checkConsistency();
163 
164     Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue));
165 }
166 
JSArray(PassRefPtr<Structure> structure,const ArgList & list)167 JSArray::JSArray(PassRefPtr<Structure> structure, const ArgList& list)
168     : JSObject(structure)
169 {
170     unsigned initialCapacity = list.size();
171 
172     m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity)));
173     m_storage->m_length = initialCapacity;
174     m_storage->m_vectorLength = initialCapacity;
175     m_storage->m_numValuesInVector = initialCapacity;
176     m_storage->m_sparseValueMap = 0;
177 
178     size_t i = 0;
179     ArgList::const_iterator end = list.end();
180     for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i)
181         m_storage->m_vector[i] = *it;
182 
183     m_fastAccessCutoff = initialCapacity;
184 
185     checkConsistency();
186 
187     Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity));
188 }
189 
~JSArray()190 JSArray::~JSArray()
191 {
192     checkConsistency(DestructorConsistencyCheck);
193 
194     delete m_storage->m_sparseValueMap;
195     fastFree(m_storage);
196 }
197 
getOwnPropertySlot(ExecState * exec,unsigned i,PropertySlot & slot)198 bool 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 < storage->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 false;
225 }
226 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)227 bool 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 
242 // ECMA 15.4.5.1
put(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)243 void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
244 {
245     bool isArrayIndex;
246     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
247     if (isArrayIndex) {
248         put(exec, i, value);
249         return;
250     }
251 
252     if (propertyName == exec->propertyNames().length) {
253         unsigned newLength = value.toUInt32(exec);
254         if (value.toNumber(exec) != static_cast<double>(newLength)) {
255             throwError(exec, RangeError, "Invalid array length.");
256             return;
257         }
258         setLength(newLength);
259         return;
260     }
261 
262     JSObject::put(exec, propertyName, value, slot);
263 }
264 
put(ExecState * exec,unsigned i,JSValue value)265 void JSArray::put(ExecState* exec, unsigned i, JSValue value)
266 {
267     checkConsistency();
268 
269     unsigned length = m_storage->m_length;
270     if (i >= length && i <= MAX_ARRAY_INDEX) {
271         length = i + 1;
272         m_storage->m_length = length;
273     }
274 
275     if (i < m_storage->m_vectorLength) {
276         JSValue& valueSlot = m_storage->m_vector[i];
277         if (valueSlot) {
278             valueSlot = value;
279             checkConsistency();
280             return;
281         }
282         valueSlot = value;
283         if (++m_storage->m_numValuesInVector == m_storage->m_length)
284             m_fastAccessCutoff = m_storage->m_length;
285         checkConsistency();
286         return;
287     }
288 
289     putSlowCase(exec, i, value);
290 }
291 
putSlowCase(ExecState * exec,unsigned i,JSValue value)292 NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value)
293 {
294     ArrayStorage* storage = m_storage;
295     SparseArrayValueMap* map = storage->m_sparseValueMap;
296 
297     if (i >= MIN_SPARSE_ARRAY_INDEX) {
298         if (i > MAX_ARRAY_INDEX) {
299             PutPropertySlot slot;
300             put(exec, Identifier::from(exec, i), value, slot);
301             return;
302         }
303 
304         // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
305         // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
306         if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
307             if (!map) {
308                 map = new SparseArrayValueMap;
309                 storage->m_sparseValueMap = map;
310             }
311             map->set(i, value);
312             return;
313         }
314     }
315 
316     // We have decided that we'll put the new item into the vector.
317     // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
318     if (!map || map->isEmpty()) {
319         if (increaseVectorLength(i + 1)) {
320             storage = m_storage;
321             storage->m_vector[i] = value;
322             if (++storage->m_numValuesInVector == storage->m_length)
323                 m_fastAccessCutoff = storage->m_length;
324             checkConsistency();
325         } else
326             throwOutOfMemoryError(exec);
327         return;
328     }
329 
330     // Decide how many values it would be best to move from the map.
331     unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
332     unsigned newVectorLength = increasedVectorLength(i + 1);
333     for (unsigned j = max(storage->m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
334         newNumValuesInVector += map->contains(j);
335     if (i >= MIN_SPARSE_ARRAY_INDEX)
336         newNumValuesInVector -= map->contains(i);
337     if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
338         unsigned proposedNewNumValuesInVector = newNumValuesInVector;
339         // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
340         while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) {
341             unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
342             for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j)
343                 proposedNewNumValuesInVector += map->contains(j);
344             if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
345                 break;
346             newVectorLength = proposedNewVectorLength;
347             newNumValuesInVector = proposedNewNumValuesInVector;
348         }
349     }
350 
351     storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength)));
352     if (!storage) {
353         throwOutOfMemoryError(exec);
354         return;
355     }
356 
357     unsigned vectorLength = storage->m_vectorLength;
358 
359     Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
360 
361     if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
362         for (unsigned j = vectorLength; j < newVectorLength; ++j)
363             storage->m_vector[j] = JSValue();
364         if (i > MIN_SPARSE_ARRAY_INDEX)
365             map->remove(i);
366     } else {
367         for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j)
368             storage->m_vector[j] = JSValue();
369         for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
370             storage->m_vector[j] = map->take(j);
371     }
372 
373     storage->m_vector[i] = value;
374 
375     storage->m_vectorLength = newVectorLength;
376     storage->m_numValuesInVector = newNumValuesInVector;
377 
378     m_storage = storage;
379 
380     checkConsistency();
381 }
382 
deleteProperty(ExecState * exec,const Identifier & propertyName)383 bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName)
384 {
385     bool isArrayIndex;
386     unsigned i = propertyName.toArrayIndex(&isArrayIndex);
387     if (isArrayIndex)
388         return deleteProperty(exec, i);
389 
390     if (propertyName == exec->propertyNames().length)
391         return false;
392 
393     return JSObject::deleteProperty(exec, propertyName);
394 }
395 
deleteProperty(ExecState * exec,unsigned i)396 bool JSArray::deleteProperty(ExecState* exec, unsigned i)
397 {
398     checkConsistency();
399 
400     ArrayStorage* storage = m_storage;
401 
402     if (i < storage->m_vectorLength) {
403         JSValue& valueSlot = storage->m_vector[i];
404         if (!valueSlot) {
405             checkConsistency();
406             return false;
407         }
408         valueSlot = JSValue();
409         --storage->m_numValuesInVector;
410         if (m_fastAccessCutoff > i)
411             m_fastAccessCutoff = i;
412         checkConsistency();
413         return true;
414     }
415 
416     if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
417         if (i >= MIN_SPARSE_ARRAY_INDEX) {
418             SparseArrayValueMap::iterator it = map->find(i);
419             if (it != map->end()) {
420                 map->remove(it);
421                 checkConsistency();
422                 return true;
423             }
424         }
425     }
426 
427     checkConsistency();
428 
429     if (i > MAX_ARRAY_INDEX)
430         return deleteProperty(exec, Identifier::from(exec, i));
431 
432     return false;
433 }
434 
getPropertyNames(ExecState * exec,PropertyNameArray & propertyNames)435 void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
436 {
437     // FIXME: Filling PropertyNameArray with an identifier for every integer
438     // is incredibly inefficient for large arrays. We need a different approach,
439     // which almost certainly means a different structure for PropertyNameArray.
440 
441     ArrayStorage* storage = m_storage;
442 
443     unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength);
444     for (unsigned i = 0; i < usedVectorLength; ++i) {
445         if (storage->m_vector[i])
446             propertyNames.add(Identifier::from(exec, i));
447     }
448 
449     if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
450         SparseArrayValueMap::iterator end = map->end();
451         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
452             propertyNames.add(Identifier::from(exec, it->first));
453     }
454 
455     JSObject::getPropertyNames(exec, propertyNames);
456 }
457 
increaseVectorLength(unsigned newLength)458 bool JSArray::increaseVectorLength(unsigned newLength)
459 {
460     // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
461     // to the vector. Callers have to account for that, because they can do it more efficiently.
462 
463     ArrayStorage* storage = m_storage;
464 
465     unsigned vectorLength = storage->m_vectorLength;
466     ASSERT(newLength > vectorLength);
467     ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
468     unsigned newVectorLength = increasedVectorLength(newLength);
469 
470     storage = static_cast<ArrayStorage*>(tryFastRealloc(storage, storageSize(newVectorLength)));
471     if (!storage)
472         return false;
473 
474     Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
475     storage->m_vectorLength = newVectorLength;
476 
477     for (unsigned i = vectorLength; i < newVectorLength; ++i)
478         storage->m_vector[i] = JSValue();
479 
480     m_storage = storage;
481     return true;
482 }
483 
setLength(unsigned newLength)484 void JSArray::setLength(unsigned newLength)
485 {
486     checkConsistency();
487 
488     ArrayStorage* storage = m_storage;
489 
490     unsigned length = m_storage->m_length;
491 
492     if (newLength < length) {
493         if (m_fastAccessCutoff > newLength)
494             m_fastAccessCutoff = newLength;
495 
496         unsigned usedVectorLength = min(length, storage->m_vectorLength);
497         for (unsigned i = newLength; i < usedVectorLength; ++i) {
498             JSValue& valueSlot = storage->m_vector[i];
499             bool hadValue = valueSlot;
500             valueSlot = JSValue();
501             storage->m_numValuesInVector -= hadValue;
502         }
503 
504         if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
505             SparseArrayValueMap copy = *map;
506             SparseArrayValueMap::iterator end = copy.end();
507             for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
508                 if (it->first >= newLength)
509                     map->remove(it->first);
510             }
511             if (map->isEmpty()) {
512                 delete map;
513                 storage->m_sparseValueMap = 0;
514             }
515         }
516     }
517 
518     m_storage->m_length = newLength;
519 
520     checkConsistency();
521 }
522 
pop()523 JSValue JSArray::pop()
524 {
525     checkConsistency();
526 
527     unsigned length = m_storage->m_length;
528     if (!length)
529         return jsUndefined();
530 
531     --length;
532 
533     JSValue result;
534 
535     if (m_fastAccessCutoff > length) {
536         JSValue& valueSlot = m_storage->m_vector[length];
537         result = valueSlot;
538         ASSERT(result);
539         valueSlot = JSValue();
540         --m_storage->m_numValuesInVector;
541         m_fastAccessCutoff = length;
542     } else if (length < m_storage->m_vectorLength) {
543         JSValue& valueSlot = m_storage->m_vector[length];
544         result = valueSlot;
545         valueSlot = JSValue();
546         if (result)
547             --m_storage->m_numValuesInVector;
548         else
549             result = jsUndefined();
550     } else {
551         result = jsUndefined();
552         if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
553             SparseArrayValueMap::iterator it = map->find(length);
554             if (it != map->end()) {
555                 result = it->second;
556                 map->remove(it);
557                 if (map->isEmpty()) {
558                     delete map;
559                     m_storage->m_sparseValueMap = 0;
560                 }
561             }
562         }
563     }
564 
565     m_storage->m_length = length;
566 
567     checkConsistency();
568 
569     return result;
570 }
571 
push(ExecState * exec,JSValue value)572 void JSArray::push(ExecState* exec, JSValue value)
573 {
574     checkConsistency();
575 
576     if (m_storage->m_length < m_storage->m_vectorLength) {
577         ASSERT(!m_storage->m_vector[m_storage->m_length]);
578         m_storage->m_vector[m_storage->m_length] = value;
579         if (++m_storage->m_numValuesInVector == ++m_storage->m_length)
580             m_fastAccessCutoff = m_storage->m_length;
581         checkConsistency();
582         return;
583     }
584 
585     if (m_storage->m_length < MIN_SPARSE_ARRAY_INDEX) {
586         SparseArrayValueMap* map = m_storage->m_sparseValueMap;
587         if (!map || map->isEmpty()) {
588             if (increaseVectorLength(m_storage->m_length + 1)) {
589                 m_storage->m_vector[m_storage->m_length] = value;
590                 if (++m_storage->m_numValuesInVector == ++m_storage->m_length)
591                     m_fastAccessCutoff = m_storage->m_length;
592                 checkConsistency();
593                 return;
594             }
595             checkConsistency();
596             throwOutOfMemoryError(exec);
597             return;
598         }
599     }
600 
601     putSlowCase(exec, m_storage->m_length++, value);
602 }
603 
markChildren(MarkStack & markStack)604 void JSArray::markChildren(MarkStack& markStack)
605 {
606     JSObject::markChildren(markStack);
607 
608     ArrayStorage* storage = m_storage;
609 
610     unsigned usedVectorLength = min(storage->m_length, storage->m_vectorLength);
611     markStack.appendValues(storage->m_vector, usedVectorLength, MayContainNullValues);
612 
613     if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
614         SparseArrayValueMap::iterator end = map->end();
615         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
616             markStack.append(it->second);
617     }
618 }
619 
compareNumbersForQSort(const void * a,const void * b)620 static int compareNumbersForQSort(const void* a, const void* b)
621 {
622     double da = static_cast<const JSValue*>(a)->uncheckedGetNumber();
623     double db = static_cast<const JSValue*>(b)->uncheckedGetNumber();
624     return (da > db) - (da < db);
625 }
626 
627 typedef std::pair<JSValue, UString> ValueStringPair;
628 
compareByStringPairForQSort(const void * a,const void * b)629 static int compareByStringPairForQSort(const void* a, const void* b)
630 {
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);
634 }
635 
sortNumeric(ExecState * exec,JSValue compareFunction,CallType callType,const CallData & callData)636 void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
637 {
638     unsigned lengthNotIncludingUndefined = compactForSorting();
639     if (m_storage->m_sparseValueMap) {
640         throwOutOfMemoryError(exec);
641         return;
642     }
643 
644     if (!lengthNotIncludingUndefined)
645         return;
646 
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;
652             break;
653         }
654     }
655 
656     if (!allValuesAreNumbers)
657         return sort(exec, compareFunction, callType, callData);
658 
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(JSValue), compareNumbersForQSort);
663 
664     checkConsistency(SortConsistencyCheck);
665 }
666 
sort(ExecState * exec)667 void JSArray::sort(ExecState* exec)
668 {
669     unsigned lengthNotIncludingUndefined = compactForSorting();
670     if (m_storage->m_sparseValueMap) {
671         throwOutOfMemoryError(exec);
672         return;
673     }
674 
675     if (!lengthNotIncludingUndefined)
676         return;
677 
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.
682 
683     Vector<ValueStringPair> values(lengthNotIncludingUndefined);
684     if (!values.begin()) {
685         throwOutOfMemoryError(exec);
686         return;
687     }
688 
689     for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
690         JSValue value = m_storage->m_vector[i];
691         ASSERT(!value.isUndefined());
692         values[i].first = value;
693     }
694 
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!
697 
698     // FIXME: The following loop continues to call toString on subsequent values even after
699     // a toString call raises an exception.
700 
701     for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
702         values[i].second = values[i].first.toString(exec);
703 
704     if (exec->hadException())
705         return;
706 
707     // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
708     // than O(N log N).
709 
710 #if HAVE(MERGESORT)
711     mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
712 #else
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);
716 #endif
717 
718     // FIXME: If the toString function changed the length of the array, this might be
719     // modifying the vector incorrectly.
720 
721     for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
722         m_storage->m_vector[i] = values[i].first;
723 
724     checkConsistency(SortConsistencyCheck);
725 }
726 
727 struct AVLTreeNodeForArrayCompare {
728     JSValue value;
729 
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.
733     int32_t gt;
734     int32_t lt;
735 };
736 
737 struct AVLTreeAbstractorForArrayCompare {
738     typedef int32_t handle; // Handle is an index into m_nodes vector.
739     typedef JSValue key;
740     typedef int32_t size;
741 
742     Vector<AVLTreeNodeForArrayCompare> m_nodes;
743     ExecState* m_exec;
744     JSValue m_compareFunction;
745     CallType m_compareCallType;
746     const CallData* m_compareCallData;
747     JSValue m_globalThisValue;
748     OwnPtr<CachedCall> m_cachedCall;
749 
get_lessJSC::AVLTreeAbstractorForArrayCompare750     handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
set_lessJSC::AVLTreeAbstractorForArrayCompare751     void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
get_greaterJSC::AVLTreeAbstractorForArrayCompare752     handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
set_greaterJSC::AVLTreeAbstractorForArrayCompare753     void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
754 
get_balance_factorJSC::AVLTreeAbstractorForArrayCompare755     int get_balance_factor(handle h)
756     {
757         if (m_nodes[h].gt & 0x80000000)
758             return -1;
759         return static_cast<unsigned>(m_nodes[h].lt) >> 31;
760     }
761 
set_balance_factorJSC::AVLTreeAbstractorForArrayCompare762     void set_balance_factor(handle h, int bf)
763     {
764         if (bf == 0) {
765             m_nodes[h].lt &= 0x7FFFFFFF;
766             m_nodes[h].gt &= 0x7FFFFFFF;
767         } else {
768             m_nodes[h].lt |= 0x80000000;
769             if (bf < 0)
770                 m_nodes[h].gt |= 0x80000000;
771             else
772                 m_nodes[h].gt &= 0x7FFFFFFF;
773         }
774     }
775 
compare_key_keyJSC::AVLTreeAbstractorForArrayCompare776     int compare_key_key(key va, key vb)
777     {
778         ASSERT(!va.isUndefined());
779         ASSERT(!vb.isUndefined());
780 
781         if (m_exec->hadException())
782             return 1;
783 
784         double compareResult;
785         if (m_cachedCall) {
786             m_cachedCall->setThis(m_globalThisValue);
787             m_cachedCall->setArgument(0, va);
788             m_cachedCall->setArgument(1, vb);
789             compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame());
790         } else {
791             MarkedArgumentBuffer arguments;
792             arguments.append(va);
793             arguments.append(vb);
794             compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments).toNumber(m_exec);
795         }
796         return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
797     }
798 
compare_key_nodeJSC::AVLTreeAbstractorForArrayCompare799     int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
compare_node_nodeJSC::AVLTreeAbstractorForArrayCompare800     int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
801 
nullJSC::AVLTreeAbstractorForArrayCompare802     static handle null() { return 0x7FFFFFFF; }
803 };
804 
sort(ExecState * exec,JSValue compareFunction,CallType callType,const CallData & callData)805 void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
806 {
807     checkConsistency();
808 
809     // FIXME: This ignores exceptions raised in the compare function or in toNumber.
810 
811     // The maximum tree depth is compiled in - but the caller is clearly up to no good
812     // if a larger array is passed.
813     ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
814     if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
815         return;
816 
817     if (!m_storage->m_length)
818         return;
819 
820     unsigned usedVectorLength = min(m_storage->m_length, m_storage->m_vectorLength);
821 
822     AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
823     tree.abstractor().m_exec = exec;
824     tree.abstractor().m_compareFunction = compareFunction;
825     tree.abstractor().m_compareCallType = callType;
826     tree.abstractor().m_compareCallData = &callData;
827     tree.abstractor().m_globalThisValue = exec->globalThisValue();
828     tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0));
829 
830     if (callType == CallTypeJS)
831         tree.abstractor().m_cachedCall.set(new CachedCall(exec, asFunction(compareFunction), 2, exec->exceptionSlot()));
832 
833     if (!tree.abstractor().m_nodes.begin()) {
834         throwOutOfMemoryError(exec);
835         return;
836     }
837 
838     // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
839     // right out from under us while we're building the tree here.
840 
841     unsigned numDefined = 0;
842     unsigned numUndefined = 0;
843 
844     // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
845     for (; numDefined < usedVectorLength; ++numDefined) {
846         JSValue v = m_storage->m_vector[numDefined];
847         if (!v || v.isUndefined())
848             break;
849         tree.abstractor().m_nodes[numDefined].value = v;
850         tree.insert(numDefined);
851     }
852     for (unsigned i = numDefined; i < usedVectorLength; ++i) {
853         JSValue v = m_storage->m_vector[i];
854         if (v) {
855             if (v.isUndefined())
856                 ++numUndefined;
857             else {
858                 tree.abstractor().m_nodes[numDefined].value = v;
859                 tree.insert(numDefined);
860                 ++numDefined;
861             }
862         }
863     }
864 
865     unsigned newUsedVectorLength = numDefined + numUndefined;
866 
867     if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
868         newUsedVectorLength += map->size();
869         if (newUsedVectorLength > m_storage->m_vectorLength) {
870             // Check that it is possible to allocate an array large enough to hold all the entries.
871             if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) {
872                 throwOutOfMemoryError(exec);
873                 return;
874             }
875         }
876 
877         SparseArrayValueMap::iterator end = map->end();
878         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
879             tree.abstractor().m_nodes[numDefined].value = it->second;
880             tree.insert(numDefined);
881             ++numDefined;
882         }
883 
884         delete map;
885         m_storage->m_sparseValueMap = 0;
886     }
887 
888     ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
889 
890     // FIXME: If the compare function changed the length of the array, the following might be
891     // modifying the vector incorrectly.
892 
893     // Copy the values back into m_storage.
894     AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
895     iter.start_iter_least(tree);
896     for (unsigned i = 0; i < numDefined; ++i) {
897         m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value;
898         ++iter;
899     }
900 
901     // Put undefined values back in.
902     for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
903         m_storage->m_vector[i] = jsUndefined();
904 
905     // Ensure that unused values in the vector are zeroed out.
906     for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
907         m_storage->m_vector[i] = JSValue();
908 
909     m_fastAccessCutoff = newUsedVectorLength;
910     m_storage->m_numValuesInVector = newUsedVectorLength;
911 
912     checkConsistency(SortConsistencyCheck);
913 }
914 
fillArgList(ExecState * exec,MarkedArgumentBuffer & args)915 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
916 {
917     unsigned fastAccessLength = min(m_storage->m_length, m_fastAccessCutoff);
918     unsigned i = 0;
919     for (; i < fastAccessLength; ++i)
920         args.append(getIndex(i));
921     for (; i < m_storage->m_length; ++i)
922         args.append(get(exec, i));
923 }
924 
copyToRegisters(ExecState * exec,Register * buffer,uint32_t maxSize)925 void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize)
926 {
927     ASSERT(m_storage->m_length == maxSize);
928     UNUSED_PARAM(maxSize);
929     unsigned fastAccessLength = min(m_storage->m_length, m_fastAccessCutoff);
930     unsigned i = 0;
931     for (; i < fastAccessLength; ++i)
932         buffer[i] = getIndex(i);
933     uint32_t size = m_storage->m_length;
934     for (; i < size; ++i)
935         buffer[i] = get(exec, i);
936 }
937 
compactForSorting()938 unsigned JSArray::compactForSorting()
939 {
940     checkConsistency();
941 
942     ArrayStorage* storage = m_storage;
943 
944     unsigned usedVectorLength = min(m_storage->m_length, storage->m_vectorLength);
945 
946     unsigned numDefined = 0;
947     unsigned numUndefined = 0;
948 
949     for (; numDefined < usedVectorLength; ++numDefined) {
950         JSValue v = storage->m_vector[numDefined];
951         if (!v || v.isUndefined())
952             break;
953     }
954     for (unsigned i = numDefined; i < usedVectorLength; ++i) {
955         JSValue v = storage->m_vector[i];
956         if (v) {
957             if (v.isUndefined())
958                 ++numUndefined;
959             else
960                 storage->m_vector[numDefined++] = v;
961         }
962     }
963 
964     unsigned newUsedVectorLength = numDefined + numUndefined;
965 
966     if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
967         newUsedVectorLength += map->size();
968         if (newUsedVectorLength > storage->m_vectorLength) {
969             // Check that it is possible to allocate an array large enough to hold all the entries - if not,
970             // exception is thrown by caller.
971             if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength))
972                 return 0;
973             storage = m_storage;
974         }
975 
976         SparseArrayValueMap::iterator end = map->end();
977         for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
978             storage->m_vector[numDefined++] = it->second;
979 
980         delete map;
981         storage->m_sparseValueMap = 0;
982     }
983 
984     for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
985         storage->m_vector[i] = jsUndefined();
986     for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
987         storage->m_vector[i] = JSValue();
988 
989     m_fastAccessCutoff = newUsedVectorLength;
990     storage->m_numValuesInVector = newUsedVectorLength;
991 
992     checkConsistency(SortConsistencyCheck);
993 
994     return numDefined;
995 }
996 
lazyCreationData()997 void* JSArray::lazyCreationData()
998 {
999     return m_storage->lazyCreationData;
1000 }
1001 
setLazyCreationData(void * d)1002 void JSArray::setLazyCreationData(void* d)
1003 {
1004     m_storage->lazyCreationData = d;
1005 }
1006 
1007 #if CHECK_ARRAY_CONSISTENCY
1008 
checkConsistency(ConsistencyCheckType type)1009 void JSArray::checkConsistency(ConsistencyCheckType type)
1010 {
1011     ASSERT(m_storage);
1012     if (type == SortConsistencyCheck)
1013         ASSERT(!m_storage->m_sparseValueMap);
1014 
1015     ASSERT(m_fastAccessCutoff <= m_storage->m_length);
1016     ASSERT(m_fastAccessCutoff <= m_storage->m_numValuesInVector);
1017 
1018     unsigned numValuesInVector = 0;
1019     for (unsigned i = 0; i < m_storage->m_vectorLength; ++i) {
1020         if (JSValue value = m_storage->m_vector[i]) {
1021             ASSERT(i < m_storage->m_length);
1022             if (type != DestructorConsistencyCheck)
1023                 value->type(); // Likely to crash if the object was deallocated.
1024             ++numValuesInVector;
1025         } else {
1026             ASSERT(i >= m_fastAccessCutoff);
1027             if (type == SortConsistencyCheck)
1028                 ASSERT(i >= m_storage->m_numValuesInVector);
1029         }
1030     }
1031     ASSERT(numValuesInVector == m_storage->m_numValuesInVector);
1032 
1033     if (m_storage->m_sparseValueMap) {
1034         SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end();
1035         for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) {
1036             unsigned index = it->first;
1037             ASSERT(index < m_storage->m_length);
1038             ASSERT(index >= m_storage->m_vectorLength);
1039             ASSERT(index <= MAX_ARRAY_INDEX);
1040             ASSERT(it->second);
1041             if (type != DestructorConsistencyCheck)
1042                 it->second->type(); // Likely to crash if the object was deallocated.
1043         }
1044     }
1045 }
1046 
1047 #endif
1048 
constructEmptyArray(ExecState * exec)1049 JSArray* constructEmptyArray(ExecState* exec)
1050 {
1051     return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure());
1052 }
1053 
constructEmptyArray(ExecState * exec,unsigned initialLength)1054 JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength)
1055 {
1056     return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), initialLength);
1057 }
1058 
constructArray(ExecState * exec,JSValue singleItemValue)1059 JSArray* constructArray(ExecState* exec, JSValue singleItemValue)
1060 {
1061     MarkedArgumentBuffer values;
1062     values.append(singleItemValue);
1063     return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values);
1064 }
1065 
constructArray(ExecState * exec,const ArgList & values)1066 JSArray* constructArray(ExecState* exec, const ArgList& values)
1067 {
1068     return new (exec) JSArray(exec->lexicalGlobalObject()->arrayStructure(), values);
1069 }
1070 
1071 } // namespace JSC
1072