source: webkit/trunk/JavaScriptCore/runtime/JSArray.cpp@ 67583

Last change on this file since 67583 was 67494, checked in by Darin Adler, 15 years ago

2010-09-14 Darin Adler <Darin Adler>

Reviewed by Geoffrey Garen.

Sort with non-numeric custom sort function fails on array with length but no values
https://bugs.webkit.org/show_bug.cgi?id=45781

  • runtime/JSArray.cpp: (JSC::JSArray::sort): Replaced early exit for an array of length zero to instead exit for any array without values, even if it has a non-0 length.

2010-09-14 Darin Adler <Darin Adler>

Reviewed by Geoffrey Garen.

Sort with non-numeric custom sort function fails on array with length but no values
https://bugs.webkit.org/show_bug.cgi?id=45781

  • fast/js/script-tests/sort-large-array.js: Added test cases.
  • fast/js/sort-large-array-expected.txt: Updated.
  • Property svn:eol-style set to native
File size: 44.3 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 * Copyright (C) 2003 Peter Kelly ([email protected])
5 * Copyright (C) 2006 Alexey Proskuryakov ([email protected])
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
36using namespace std;
37using namespace WTF;
38
39namespace JSC {
40
41ASSERT_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// The value BASE_VECTOR_LEN is the maximum number of vector elements we'll allocate
82// for an array that was created with a sepcified length (e.g. a = new Array(123))
83#define BASE_VECTOR_LEN 4U
84
85// The upper bound to the size we'll grow a zero length array when the first element
86// is added.
87#define FIRST_VECTOR_GROW 4U
88
89// Our policy for when to use a vector and when to use a sparse map.
90// For all array indices under MIN_SPARSE_ARRAY_INDEX, we always use a vector.
91// When indices greater than MIN_SPARSE_ARRAY_INDEX are involved, we use a vector
92// as long as it is 1/8 full. If more sparse than that, we use a map.
93static const unsigned minDensityMultiplier = 8;
94
95const ClassInfo JSArray::info = {"Array", 0, 0, 0};
96
97// We keep track of the size of the last array after it was grown. We use this
98// as a simple heuristic for as the value to grow the next array from size 0.
99// This value is capped by the constant FIRST_VECTOR_GROW defined above.
100static unsigned lastArraySize = 0;
101
102static inline size_t storageSize(unsigned vectorLength)
103{
104 ASSERT(vectorLength <= MAX_STORAGE_VECTOR_LENGTH);
105
106 // MAX_STORAGE_VECTOR_LENGTH is defined such that provided (vectorLength <= MAX_STORAGE_VECTOR_LENGTH)
107 // - as asserted above - the following calculation cannot overflow.
108 size_t size = (sizeof(ArrayStorage) - sizeof(JSValue)) + (vectorLength * sizeof(JSValue));
109 // Assertion to detect integer overflow in previous calculation (should not be possible, provided that
110 // MAX_STORAGE_VECTOR_LENGTH is correctly defined).
111 ASSERT(((size - (sizeof(ArrayStorage) - sizeof(JSValue))) / sizeof(JSValue) == vectorLength) && (size >= (sizeof(ArrayStorage) - sizeof(JSValue))));
112
113 return size;
114}
115
116static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
117{
118 return length / minDensityMultiplier <= numValues;
119}
120
121#if !CHECK_ARRAY_CONSISTENCY
122
123inline void JSArray::checkConsistency(ConsistencyCheckType)
124{
125}
126
127#endif
128
129JSArray::JSArray(VPtrStealingHackType)
130 : JSObject(createStructure(jsNull()))
131{
132 unsigned initialCapacity = 0;
133
134 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
135 m_storage->m_allocBase = m_storage;
136 m_indexBias = 0;
137 m_vectorLength = initialCapacity;
138
139 checkConsistency();
140
141 // It's not safe to call Heap::heap(this) in order to report extra memory
142 // cost here, because the VPtrStealingHackType JSArray is not allocated on
143 // the heap. For the same reason, it's OK not to report extra cost.
144}
145
146JSArray::JSArray(NonNullPassRefPtr<Structure> structure)
147 : JSObject(structure)
148{
149 unsigned initialCapacity = 0;
150
151 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
152 m_storage->m_allocBase = m_storage;
153 m_indexBias = 0;
154 m_vectorLength = initialCapacity;
155
156 checkConsistency();
157
158 Heap::heap(this)->reportExtraMemoryCost(storageSize(0));
159}
160
161JSArray::JSArray(NonNullPassRefPtr<Structure> structure, unsigned initialLength, ArrayCreationMode creationMode)
162 : JSObject(structure)
163{
164 unsigned initialCapacity;
165 if (creationMode == CreateCompact)
166 initialCapacity = initialLength;
167 else
168 initialCapacity = min(BASE_VECTOR_LEN, MIN_SPARSE_ARRAY_INDEX);
169
170 m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity)));
171 m_storage->m_allocBase = m_storage;
172 m_storage->m_length = initialLength;
173 m_indexBias = 0;
174 m_vectorLength = initialCapacity;
175 m_storage->m_sparseValueMap = 0;
176 m_storage->subclassData = 0;
177 m_storage->reportedMapCapacity = 0;
178
179 if (creationMode == CreateCompact) {
180#if CHECK_ARRAY_CONSISTENCY
181 m_storage->m_inCompactInitialization = !!initialCapacity;
182#endif
183 m_storage->m_length = 0;
184 m_storage->m_numValuesInVector = initialCapacity;
185 } else {
186#if CHECK_ARRAY_CONSISTENCY
187 storage->m_inCompactInitialization = false;
188#endif
189 m_storage->m_length = initialLength;
190 m_storage->m_numValuesInVector = 0;
191 JSValue* vector = m_storage->m_vector;
192 for (size_t i = 0; i < initialCapacity; ++i)
193 vector[i] = JSValue();
194 }
195
196 checkConsistency();
197
198 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity));
199}
200
201JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list)
202 : JSObject(structure)
203{
204 unsigned initialCapacity = list.size();
205
206 m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity)));
207 m_storage->m_allocBase = m_storage;
208 m_indexBias = 0;
209 m_storage->m_length = initialCapacity;
210 m_vectorLength = initialCapacity;
211 m_storage->m_numValuesInVector = initialCapacity;
212 m_storage->m_sparseValueMap = 0;
213 m_storage->subclassData = 0;
214 m_storage->reportedMapCapacity = 0;
215#if CHECK_ARRAY_CONSISTENCY
216 m_storage->m_inCompactInitialization = false;
217#endif
218
219 size_t i = 0;
220 JSValue* vector = m_storage->m_vector;
221 ArgList::const_iterator end = list.end();
222 for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i)
223 vector[i] = *it;
224
225 checkConsistency();
226
227 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity));
228}
229
230JSArray::~JSArray()
231{
232 ASSERT(vptr() == JSGlobalData::jsArrayVPtr);
233 checkConsistency(DestructorConsistencyCheck);
234
235 delete m_storage->m_sparseValueMap;
236 fastFree(m_storage->m_allocBase);
237}
238
239bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
240{
241 ArrayStorage* storage = m_storage;
242
243 if (i >= storage->m_length) {
244 if (i > MAX_ARRAY_INDEX)
245 return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
246 return false;
247 }
248
249 if (i < m_vectorLength) {
250 JSValue& valueSlot = storage->m_vector[i];
251 if (valueSlot) {
252 slot.setValueSlot(&valueSlot);
253 return true;
254 }
255 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
256 if (i >= MIN_SPARSE_ARRAY_INDEX) {
257 SparseArrayValueMap::iterator it = map->find(i);
258 if (it != map->end()) {
259 slot.setValueSlot(&it->second);
260 return true;
261 }
262 }
263 }
264
265 return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
266}
267
268bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
269{
270 if (propertyName == exec->propertyNames().length) {
271 slot.setValue(jsNumber(exec, length()));
272 return true;
273 }
274
275 bool isArrayIndex;
276 unsigned i = propertyName.toArrayIndex(isArrayIndex);
277 if (isArrayIndex)
278 return JSArray::getOwnPropertySlot(exec, i, slot);
279
280 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
281}
282
283bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
284{
285 if (propertyName == exec->propertyNames().length) {
286 descriptor.setDescriptor(jsNumber(exec, length()), DontDelete | DontEnum);
287 return true;
288 }
289
290 ArrayStorage* storage = m_storage;
291
292 bool isArrayIndex;
293 unsigned i = propertyName.toArrayIndex(isArrayIndex);
294 if (isArrayIndex) {
295 if (i >= storage->m_length)
296 return false;
297 if (i < m_vectorLength) {
298 JSValue& value = storage->m_vector[i];
299 if (value) {
300 descriptor.setDescriptor(value, 0);
301 return true;
302 }
303 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
304 if (i >= MIN_SPARSE_ARRAY_INDEX) {
305 SparseArrayValueMap::iterator it = map->find(i);
306 if (it != map->end()) {
307 descriptor.setDescriptor(it->second, 0);
308 return true;
309 }
310 }
311 }
312 }
313 return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
314}
315
316// ECMA 15.4.5.1
317void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
318{
319 bool isArrayIndex;
320 unsigned i = propertyName.toArrayIndex(isArrayIndex);
321 if (isArrayIndex) {
322 put(exec, i, value);
323 return;
324 }
325
326 if (propertyName == exec->propertyNames().length) {
327 unsigned newLength = value.toUInt32(exec);
328 if (value.toNumber(exec) != static_cast<double>(newLength)) {
329 throwError(exec, createRangeError(exec, "Invalid array length."));
330 return;
331 }
332 setLength(newLength);
333 return;
334 }
335
336 JSObject::put(exec, propertyName, value, slot);
337}
338
339void JSArray::put(ExecState* exec, unsigned i, JSValue value)
340{
341 checkConsistency();
342
343 ArrayStorage* storage = m_storage;
344
345 unsigned length = storage->m_length;
346 if (i >= length && i <= MAX_ARRAY_INDEX) {
347 length = i + 1;
348 storage->m_length = length;
349 }
350
351 if (i < m_vectorLength) {
352 JSValue& valueSlot = storage->m_vector[i];
353 if (valueSlot) {
354 valueSlot = value;
355 checkConsistency();
356 return;
357 }
358 valueSlot = value;
359 ++storage->m_numValuesInVector;
360 checkConsistency();
361 return;
362 }
363
364 putSlowCase(exec, i, value);
365}
366
367NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value)
368{
369 ArrayStorage* storage = m_storage;
370
371 SparseArrayValueMap* map = storage->m_sparseValueMap;
372
373 if (i >= MIN_SPARSE_ARRAY_INDEX) {
374 if (i > MAX_ARRAY_INDEX) {
375 PutPropertySlot slot;
376 put(exec, Identifier::from(exec, i), value, slot);
377 return;
378 }
379
380 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
381 // (which will only be compacted as we reach indices that are less than MIN_SPARSE_ARRAY_INDEX) - but this makes the check much faster.
382 if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
383 if (!map) {
384 map = new SparseArrayValueMap;
385 storage->m_sparseValueMap = map;
386 }
387
388 pair<SparseArrayValueMap::iterator, bool> result = map->add(i, value);
389 if (!result.second) { // pre-existing entry
390 result.first->second = value;
391 return;
392 }
393
394 size_t capacity = map->capacity();
395 if (capacity != storage->reportedMapCapacity) {
396 Heap::heap(this)->reportExtraMemoryCost((capacity - storage->reportedMapCapacity) * (sizeof(unsigned) + sizeof(JSValue)));
397 storage->reportedMapCapacity = capacity;
398 }
399 return;
400 }
401 }
402
403 // We have decided that we'll put the new item into the vector.
404 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
405 if (!map || map->isEmpty()) {
406 if (increaseVectorLength(i + 1)) {
407 storage = m_storage;
408 storage->m_vector[i] = value;
409 ++storage->m_numValuesInVector;
410 checkConsistency();
411 } else
412 throwOutOfMemoryError(exec);
413 return;
414 }
415
416 // Decide how many values it would be best to move from the map.
417 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
418 unsigned newVectorLength = getNewVectorLength(i + 1);
419 for (unsigned j = max(m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
420 newNumValuesInVector += map->contains(j);
421 if (i >= MIN_SPARSE_ARRAY_INDEX)
422 newNumValuesInVector -= map->contains(i);
423 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
424 unsigned needLength = max(i + 1, storage->m_length);
425 unsigned proposedNewNumValuesInVector = newNumValuesInVector;
426 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
427 while ((newVectorLength < needLength) && (newVectorLength < MAX_STORAGE_VECTOR_LENGTH)) {
428 unsigned proposedNewVectorLength = getNewVectorLength(newVectorLength + 1);
429 for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j)
430 proposedNewNumValuesInVector += map->contains(j);
431 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
432 break;
433 newVectorLength = proposedNewVectorLength;
434 newNumValuesInVector = proposedNewNumValuesInVector;
435 }
436 }
437
438 void* baseStorage = storage->m_allocBase;
439
440 if (!tryFastRealloc(baseStorage, storageSize(newVectorLength + m_indexBias)).getValue(baseStorage)) {
441 throwOutOfMemoryError(exec);
442 return;
443 }
444
445 m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + m_indexBias * sizeof(JSValue));
446 m_storage->m_allocBase = baseStorage;
447 storage = m_storage;
448
449 unsigned vectorLength = m_vectorLength;
450 JSValue* vector = storage->m_vector;
451
452 if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
453 for (unsigned j = vectorLength; j < newVectorLength; ++j)
454 vector[j] = JSValue();
455 if (i > MIN_SPARSE_ARRAY_INDEX)
456 map->remove(i);
457 } else {
458 for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j)
459 vector[j] = JSValue();
460 for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
461 vector[j] = map->take(j);
462 }
463
464 ASSERT(i < newVectorLength);
465
466 m_vectorLength = newVectorLength;
467 storage->m_numValuesInVector = newNumValuesInVector;
468
469 storage->m_vector[i] = value;
470
471 checkConsistency();
472
473 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
474}
475
476bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName)
477{
478 bool isArrayIndex;
479 unsigned i = propertyName.toArrayIndex(isArrayIndex);
480 if (isArrayIndex)
481 return deleteProperty(exec, i);
482
483 if (propertyName == exec->propertyNames().length)
484 return false;
485
486 return JSObject::deleteProperty(exec, propertyName);
487}
488
489bool JSArray::deleteProperty(ExecState* exec, unsigned i)
490{
491 checkConsistency();
492
493 ArrayStorage* storage = m_storage;
494
495 if (i < m_vectorLength) {
496 JSValue& valueSlot = storage->m_vector[i];
497 if (!valueSlot) {
498 checkConsistency();
499 return false;
500 }
501 valueSlot = JSValue();
502 --storage->m_numValuesInVector;
503 checkConsistency();
504 return true;
505 }
506
507 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
508 if (i >= MIN_SPARSE_ARRAY_INDEX) {
509 SparseArrayValueMap::iterator it = map->find(i);
510 if (it != map->end()) {
511 map->remove(it);
512 checkConsistency();
513 return true;
514 }
515 }
516 }
517
518 checkConsistency();
519
520 if (i > MAX_ARRAY_INDEX)
521 return deleteProperty(exec, Identifier::from(exec, i));
522
523 return false;
524}
525
526void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
527{
528 // FIXME: Filling PropertyNameArray with an identifier for every integer
529 // is incredibly inefficient for large arrays. We need a different approach,
530 // which almost certainly means a different structure for PropertyNameArray.
531
532 ArrayStorage* storage = m_storage;
533
534 unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
535 for (unsigned i = 0; i < usedVectorLength; ++i) {
536 if (storage->m_vector[i])
537 propertyNames.add(Identifier::from(exec, i));
538 }
539
540 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
541 SparseArrayValueMap::iterator end = map->end();
542 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
543 propertyNames.add(Identifier::from(exec, it->first));
544 }
545
546 if (mode == IncludeDontEnumProperties)
547 propertyNames.add(exec->propertyNames().length);
548
549 JSObject::getOwnPropertyNames(exec, propertyNames, mode);
550}
551
552ALWAYS_INLINE unsigned JSArray::getNewVectorLength(unsigned desiredLength)
553{
554 ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
555
556 unsigned increasedLength;
557 unsigned maxInitLength = min(m_storage->m_length, 100000U);
558
559 if (desiredLength < maxInitLength)
560 increasedLength = maxInitLength;
561 else if (!m_vectorLength)
562 increasedLength = max(desiredLength, lastArraySize);
563 else {
564 // Mathematically equivalent to:
565 // increasedLength = (newLength * 3 + 1) / 2;
566 // or:
567 // increasedLength = (unsigned)ceil(newLength * 1.5));
568 // This form is not prone to internal overflow.
569 increasedLength = desiredLength + (desiredLength >> 1) + (desiredLength & 1);
570 }
571
572 ASSERT(increasedLength >= desiredLength);
573
574 lastArraySize = min(increasedLength, FIRST_VECTOR_GROW);
575
576 return min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
577}
578
579bool JSArray::increaseVectorLength(unsigned newLength)
580{
581 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
582 // to the vector. Callers have to account for that, because they can do it more efficiently.
583
584 ArrayStorage* storage = m_storage;
585
586 unsigned vectorLength = m_vectorLength;
587 ASSERT(newLength > vectorLength);
588 ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
589 unsigned newVectorLength = getNewVectorLength(newLength);
590 void* baseStorage = storage->m_allocBase;
591
592 if (!tryFastRealloc(baseStorage, storageSize(newVectorLength + m_indexBias)).getValue(baseStorage))
593 return false;
594
595 storage = m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(baseStorage) + m_indexBias * sizeof(JSValue));
596 m_storage->m_allocBase = baseStorage;
597
598 JSValue* vector = storage->m_vector;
599 for (unsigned i = vectorLength; i < newVectorLength; ++i)
600 vector[i] = JSValue();
601
602 m_vectorLength = newVectorLength;
603
604 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
605
606 return true;
607}
608
609bool JSArray::increaseVectorPrefixLength(unsigned newLength)
610{
611 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
612 // to the vector. Callers have to account for that, because they can do it more efficiently.
613
614 ArrayStorage* storage = m_storage;
615
616 unsigned vectorLength = m_vectorLength;
617 ASSERT(newLength > vectorLength);
618 ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
619 unsigned newVectorLength = getNewVectorLength(newLength);
620
621 void* newBaseStorage = fastMalloc(storageSize(newVectorLength + m_indexBias));
622 if (!newBaseStorage)
623 return false;
624
625 m_indexBias += newVectorLength - newLength;
626
627 m_storage = reinterpret_cast_ptr<ArrayStorage*>(static_cast<char*>(newBaseStorage) + m_indexBias * sizeof(JSValue));
628
629 memcpy(m_storage, storage, storageSize(0));
630 memcpy(&m_storage->m_vector[newLength - m_vectorLength], &storage->m_vector[0], vectorLength * sizeof(JSValue));
631
632 m_storage->m_allocBase = newBaseStorage;
633 m_vectorLength = newLength;
634
635 fastFree(storage->m_allocBase);
636
637 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
638
639 return true;
640}
641
642
643void JSArray::setLength(unsigned newLength)
644{
645 ArrayStorage* storage = m_storage;
646
647#if CHECK_ARRAY_CONSISTENCY
648 if (!storage->m_inCompactInitialization)
649 checkConsistency();
650 else
651 storage->m_inCompactInitialization = false;
652#endif
653
654 unsigned length = storage->m_length;
655
656 if (newLength < length) {
657 unsigned usedVectorLength = min(length, m_vectorLength);
658 for (unsigned i = newLength; i < usedVectorLength; ++i) {
659 JSValue& valueSlot = storage->m_vector[i];
660 bool hadValue = valueSlot;
661 valueSlot = JSValue();
662 storage->m_numValuesInVector -= hadValue;
663 }
664
665 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
666 SparseArrayValueMap copy = *map;
667 SparseArrayValueMap::iterator end = copy.end();
668 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
669 if (it->first >= newLength)
670 map->remove(it->first);
671 }
672 if (map->isEmpty()) {
673 delete map;
674 storage->m_sparseValueMap = 0;
675 }
676 }
677 }
678
679 storage->m_length = newLength;
680
681 checkConsistency();
682}
683
684JSValue JSArray::pop()
685{
686 checkConsistency();
687
688 ArrayStorage* storage = m_storage;
689
690 unsigned length = storage->m_length;
691 if (!length)
692 return jsUndefined();
693
694 --length;
695
696 JSValue result;
697
698 if (length < m_vectorLength) {
699 JSValue& valueSlot = storage->m_vector[length];
700 if (valueSlot) {
701 --storage->m_numValuesInVector;
702 result = valueSlot;
703 valueSlot = JSValue();
704 } else
705 result = jsUndefined();
706 } else {
707 result = jsUndefined();
708 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
709 SparseArrayValueMap::iterator it = map->find(length);
710 if (it != map->end()) {
711 result = it->second;
712 map->remove(it);
713 if (map->isEmpty()) {
714 delete map;
715 storage->m_sparseValueMap = 0;
716 }
717 }
718 }
719 }
720
721 storage->m_length = length;
722
723 checkConsistency();
724
725 return result;
726}
727
728void JSArray::push(ExecState* exec, JSValue value)
729{
730 checkConsistency();
731
732 ArrayStorage* storage = m_storage;
733
734 if (storage->m_length < m_vectorLength) {
735 storage->m_vector[storage->m_length] = value;
736 ++storage->m_numValuesInVector;
737 ++storage->m_length;
738 checkConsistency();
739 return;
740 }
741
742 if (storage->m_length < MIN_SPARSE_ARRAY_INDEX) {
743 SparseArrayValueMap* map = storage->m_sparseValueMap;
744 if (!map || map->isEmpty()) {
745 if (increaseVectorLength(storage->m_length + 1)) {
746 storage = m_storage;
747 storage->m_vector[storage->m_length] = value;
748 ++storage->m_numValuesInVector;
749 ++storage->m_length;
750 checkConsistency();
751 return;
752 }
753 checkConsistency();
754 throwOutOfMemoryError(exec);
755 return;
756 }
757 }
758
759 putSlowCase(exec, storage->m_length++, value);
760}
761
762void JSArray::shiftCount(ExecState* exec, int count)
763{
764 ASSERT(count > 0);
765
766 ArrayStorage* storage = m_storage;
767
768 unsigned oldLength = storage->m_length;
769
770 if (!oldLength)
771 return;
772
773 if (oldLength != storage->m_numValuesInVector) {
774 // If m_length and m_numValuesInVector aren't the same, we have a sparse vector
775 // which means we need to go through each entry looking for the the "empty"
776 // slots and then fill them with possible properties. See ECMA spec.
777 // 15.4.4.9 steps 11 through 13.
778 for (unsigned i = count; i < oldLength; ++i) {
779 if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) {
780 PropertySlot slot(this);
781 JSValue p = prototype();
782 if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot)))
783 put(exec, i, slot.getValue(exec, i));
784 }
785 }
786
787 storage = m_storage; // The put() above could have grown the vector and realloc'ed storage.
788
789 // Need to decrement numValuesInvector based on number of real entries
790 for (unsigned i = 0; i < (unsigned)count; ++i)
791 if ((i < m_vectorLength) && (storage->m_vector[i]))
792 --storage->m_numValuesInVector;
793 } else
794 storage->m_numValuesInVector -= count;
795
796 storage->m_length -= count;
797
798 if (m_vectorLength) {
799 count = min(m_vectorLength, (unsigned)count);
800
801 m_vectorLength -= count;
802
803 if (m_vectorLength) {
804 char* newBaseStorage = reinterpret_cast<char*>(storage) + count * sizeof(JSValue);
805 memmove(newBaseStorage, storage, storageSize(0));
806 m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);
807
808 m_indexBias += count;
809 }
810 }
811}
812
813void JSArray::unshiftCount(ExecState* exec, int count)
814{
815 ArrayStorage* storage = m_storage;
816
817 ASSERT(m_indexBias >= 0);
818 ASSERT(count >= 0);
819
820 unsigned length = storage->m_length;
821
822 if (length != storage->m_numValuesInVector) {
823 // If m_length and m_numValuesInVector aren't the same, we have a sparse vector
824 // which means we need to go through each entry looking for the the "empty"
825 // slots and then fill them with possible properties. See ECMA spec.
826 // 15.4.4.13 steps 8 through 10.
827 for (unsigned i = 0; i < length; ++i) {
828 if ((i >= m_vectorLength) || (!m_storage->m_vector[i])) {
829 PropertySlot slot(this);
830 JSValue p = prototype();
831 if ((!p.isNull()) && (asObject(p)->getPropertySlot(exec, i, slot)))
832 put(exec, i, slot.getValue(exec, i));
833 }
834 }
835 }
836
837 storage = m_storage; // The put() above could have grown the vector and realloc'ed storage.
838
839 if (m_indexBias >= count) {
840 m_indexBias -= count;
841 char* newBaseStorage = reinterpret_cast<char*>(storage) - count * sizeof(JSValue);
842 memmove(newBaseStorage, storage, storageSize(0));
843 m_storage = reinterpret_cast_ptr<ArrayStorage*>(newBaseStorage);
844 m_vectorLength += count;
845 } else if (!increaseVectorPrefixLength(m_vectorLength + count)) {
846 throwOutOfMemoryError(exec);
847 return;
848 }
849
850 JSValue* vector = m_storage->m_vector;
851 for (int i = 0; i < count; i++)
852 vector[i] = JSValue();
853}
854
855void JSArray::markChildren(MarkStack& markStack)
856{
857 markChildrenDirect(markStack);
858}
859
860static int compareNumbersForQSort(const void* a, const void* b)
861{
862 double da = static_cast<const JSValue*>(a)->uncheckedGetNumber();
863 double db = static_cast<const JSValue*>(b)->uncheckedGetNumber();
864 return (da > db) - (da < db);
865}
866
867typedef std::pair<JSValue, UString> ValueStringPair;
868
869static int compareByStringPairForQSort(const void* a, const void* b)
870{
871 const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
872 const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
873 return codePointCompare(va->second, vb->second);
874}
875
876void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
877{
878 ArrayStorage* storage = m_storage;
879
880 unsigned lengthNotIncludingUndefined = compactForSorting();
881 if (storage->m_sparseValueMap) {
882 throwOutOfMemoryError(exec);
883 return;
884 }
885
886 if (!lengthNotIncludingUndefined)
887 return;
888
889 bool allValuesAreNumbers = true;
890 size_t size = storage->m_numValuesInVector;
891 for (size_t i = 0; i < size; ++i) {
892 if (!storage->m_vector[i].isNumber()) {
893 allValuesAreNumbers = false;
894 break;
895 }
896 }
897
898 if (!allValuesAreNumbers)
899 return sort(exec, compareFunction, callType, callData);
900
901 // For numeric comparison, which is fast, qsort is faster than mergesort. We
902 // also don't require mergesort's stability, since there's no user visible
903 // side-effect from swapping the order of equal primitive values.
904 qsort(storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort);
905
906 checkConsistency(SortConsistencyCheck);
907}
908
909void JSArray::sort(ExecState* exec)
910{
911 ArrayStorage* storage = m_storage;
912
913 unsigned lengthNotIncludingUndefined = compactForSorting();
914 if (storage->m_sparseValueMap) {
915 throwOutOfMemoryError(exec);
916 return;
917 }
918
919 if (!lengthNotIncludingUndefined)
920 return;
921
922 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
923 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
924 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
925 // random or otherwise changing results, effectively making compare function inconsistent.
926
927 Vector<ValueStringPair> values(lengthNotIncludingUndefined);
928 if (!values.begin()) {
929 throwOutOfMemoryError(exec);
930 return;
931 }
932
933 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
934 JSValue value = storage->m_vector[i];
935 ASSERT(!value.isUndefined());
936 values[i].first = value;
937 }
938
939 // FIXME: While calling these toString functions, the array could be mutated.
940 // In that case, objects pointed to by values in this vector might get garbage-collected!
941
942 // FIXME: The following loop continues to call toString on subsequent values even after
943 // a toString call raises an exception.
944
945 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
946 values[i].second = values[i].first.toString(exec);
947
948 if (exec->hadException())
949 return;
950
951 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
952 // than O(N log N).
953
954#if HAVE(MERGESORT)
955 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
956#else
957 // FIXME: The qsort library function is likely to not be a stable sort.
958 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
959 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
960#endif
961
962 // FIXME: If the toString function changed the length of the array, this might be
963 // modifying the vector incorrectly.
964
965 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
966 storage->m_vector[i] = values[i].first;
967
968 checkConsistency(SortConsistencyCheck);
969}
970
971struct AVLTreeNodeForArrayCompare {
972 JSValue value;
973
974 // Child pointers. The high bit of gt is robbed and used as the
975 // balance factor sign. The high bit of lt is robbed and used as
976 // the magnitude of the balance factor.
977 int32_t gt;
978 int32_t lt;
979};
980
981struct AVLTreeAbstractorForArrayCompare {
982 typedef int32_t handle; // Handle is an index into m_nodes vector.
983 typedef JSValue key;
984 typedef int32_t size;
985
986 Vector<AVLTreeNodeForArrayCompare> m_nodes;
987 ExecState* m_exec;
988 JSValue m_compareFunction;
989 CallType m_compareCallType;
990 const CallData* m_compareCallData;
991 JSValue m_globalThisValue;
992 OwnPtr<CachedCall> m_cachedCall;
993
994 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
995 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
996 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
997 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
998
999 int get_balance_factor(handle h)
1000 {
1001 if (m_nodes[h].gt & 0x80000000)
1002 return -1;
1003 return static_cast<unsigned>(m_nodes[h].lt) >> 31;
1004 }
1005
1006 void set_balance_factor(handle h, int bf)
1007 {
1008 if (bf == 0) {
1009 m_nodes[h].lt &= 0x7FFFFFFF;
1010 m_nodes[h].gt &= 0x7FFFFFFF;
1011 } else {
1012 m_nodes[h].lt |= 0x80000000;
1013 if (bf < 0)
1014 m_nodes[h].gt |= 0x80000000;
1015 else
1016 m_nodes[h].gt &= 0x7FFFFFFF;
1017 }
1018 }
1019
1020 int compare_key_key(key va, key vb)
1021 {
1022 ASSERT(!va.isUndefined());
1023 ASSERT(!vb.isUndefined());
1024
1025 if (m_exec->hadException())
1026 return 1;
1027
1028 double compareResult;
1029 if (m_cachedCall) {
1030 m_cachedCall->setThis(m_globalThisValue);
1031 m_cachedCall->setArgument(0, va);
1032 m_cachedCall->setArgument(1, vb);
1033 compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec));
1034 } else {
1035 MarkedArgumentBuffer arguments;
1036 arguments.append(va);
1037 arguments.append(vb);
1038 compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments).toNumber(m_exec);
1039 }
1040 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1041 }
1042
1043 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
1044 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
1045
1046 static handle null() { return 0x7FFFFFFF; }
1047};
1048
1049void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1050{
1051 checkConsistency();
1052
1053 ArrayStorage* storage = m_storage;
1054
1055 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
1056
1057 // The maximum tree depth is compiled in - but the caller is clearly up to no good
1058 // if a larger array is passed.
1059 ASSERT(storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
1060 if (storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
1061 return;
1062
1063 unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
1064 unsigned nodeCount = usedVectorLength + (storage->m_sparseValueMap ? storage->m_sparseValueMap->size() : 0);
1065
1066 if (!nodeCount)
1067 return;
1068
1069 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
1070 tree.abstractor().m_exec = exec;
1071 tree.abstractor().m_compareFunction = compareFunction;
1072 tree.abstractor().m_compareCallType = callType;
1073 tree.abstractor().m_compareCallData = &callData;
1074 tree.abstractor().m_globalThisValue = exec->globalThisValue();
1075 tree.abstractor().m_nodes.grow(nodeCount);
1076
1077 if (callType == CallTypeJS)
1078 tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, asFunction(compareFunction), 2, exec->exceptionSlot()));
1079
1080 if (!tree.abstractor().m_nodes.begin()) {
1081 throwOutOfMemoryError(exec);
1082 return;
1083 }
1084
1085 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1086 // right out from under us while we're building the tree here.
1087
1088 unsigned numDefined = 0;
1089 unsigned numUndefined = 0;
1090
1091 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1092 for (; numDefined < usedVectorLength; ++numDefined) {
1093 JSValue v = storage->m_vector[numDefined];
1094 if (!v || v.isUndefined())
1095 break;
1096 tree.abstractor().m_nodes[numDefined].value = v;
1097 tree.insert(numDefined);
1098 }
1099 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1100 JSValue v = storage->m_vector[i];
1101 if (v) {
1102 if (v.isUndefined())
1103 ++numUndefined;
1104 else {
1105 tree.abstractor().m_nodes[numDefined].value = v;
1106 tree.insert(numDefined);
1107 ++numDefined;
1108 }
1109 }
1110 }
1111
1112 unsigned newUsedVectorLength = numDefined + numUndefined;
1113
1114 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
1115 newUsedVectorLength += map->size();
1116 if (newUsedVectorLength > m_vectorLength) {
1117 // Check that it is possible to allocate an array large enough to hold all the entries.
1118 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) {
1119 throwOutOfMemoryError(exec);
1120 return;
1121 }
1122 }
1123
1124 storage = m_storage;
1125
1126 SparseArrayValueMap::iterator end = map->end();
1127 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
1128 tree.abstractor().m_nodes[numDefined].value = it->second;
1129 tree.insert(numDefined);
1130 ++numDefined;
1131 }
1132
1133 delete map;
1134 storage->m_sparseValueMap = 0;
1135 }
1136
1137 ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
1138
1139 // FIXME: If the compare function changed the length of the array, the following might be
1140 // modifying the vector incorrectly.
1141
1142 // Copy the values back into m_storage.
1143 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
1144 iter.start_iter_least(tree);
1145 for (unsigned i = 0; i < numDefined; ++i) {
1146 storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value;
1147 ++iter;
1148 }
1149
1150 // Put undefined values back in.
1151 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
1152 storage->m_vector[i] = jsUndefined();
1153
1154 // Ensure that unused values in the vector are zeroed out.
1155 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
1156 storage->m_vector[i] = JSValue();
1157
1158 storage->m_numValuesInVector = newUsedVectorLength;
1159
1160 checkConsistency(SortConsistencyCheck);
1161}
1162
1163void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1164{
1165 ArrayStorage* storage = m_storage;
1166
1167 JSValue* vector = storage->m_vector;
1168 unsigned vectorEnd = min(storage->m_length, m_vectorLength);
1169 unsigned i = 0;
1170 for (; i < vectorEnd; ++i) {
1171 JSValue& v = vector[i];
1172 if (!v)
1173 break;
1174 args.append(v);
1175 }
1176
1177 for (; i < storage->m_length; ++i)
1178 args.append(get(exec, i));
1179}
1180
1181void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize)
1182{
1183 ASSERT(m_storage->m_length >= maxSize);
1184 UNUSED_PARAM(maxSize);
1185 JSValue* vector = m_storage->m_vector;
1186 unsigned vectorEnd = min(maxSize, m_vectorLength);
1187 unsigned i = 0;
1188 for (; i < vectorEnd; ++i) {
1189 JSValue& v = vector[i];
1190 if (!v)
1191 break;
1192 buffer[i] = v;
1193 }
1194
1195 for (; i < maxSize; ++i)
1196 buffer[i] = get(exec, i);
1197}
1198
1199unsigned JSArray::compactForSorting()
1200{
1201 checkConsistency();
1202
1203 ArrayStorage* storage = m_storage;
1204
1205 unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
1206
1207 unsigned numDefined = 0;
1208 unsigned numUndefined = 0;
1209
1210 for (; numDefined < usedVectorLength; ++numDefined) {
1211 JSValue v = storage->m_vector[numDefined];
1212 if (!v || v.isUndefined())
1213 break;
1214 }
1215 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1216 JSValue v = storage->m_vector[i];
1217 if (v) {
1218 if (v.isUndefined())
1219 ++numUndefined;
1220 else
1221 storage->m_vector[numDefined++] = v;
1222 }
1223 }
1224
1225 unsigned newUsedVectorLength = numDefined + numUndefined;
1226
1227 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
1228 newUsedVectorLength += map->size();
1229 if (newUsedVectorLength > m_vectorLength) {
1230 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
1231 // exception is thrown by caller.
1232 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength))
1233 return 0;
1234
1235 storage = m_storage;
1236 }
1237
1238 SparseArrayValueMap::iterator end = map->end();
1239 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
1240 storage->m_vector[numDefined++] = it->second;
1241
1242 delete map;
1243 storage->m_sparseValueMap = 0;
1244 }
1245
1246 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
1247 storage->m_vector[i] = jsUndefined();
1248 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
1249 storage->m_vector[i] = JSValue();
1250
1251 storage->m_numValuesInVector = newUsedVectorLength;
1252
1253 checkConsistency(SortConsistencyCheck);
1254
1255 return numDefined;
1256}
1257
1258void* JSArray::subclassData() const
1259{
1260 return m_storage->subclassData;
1261}
1262
1263void JSArray::setSubclassData(void* d)
1264{
1265 m_storage->subclassData = d;
1266}
1267
1268#if CHECK_ARRAY_CONSISTENCY
1269
1270void JSArray::checkConsistency(ConsistencyCheckType type)
1271{
1272 ArrayStorage* storage = m_storage;
1273
1274 ASSERT(storage);
1275 if (type == SortConsistencyCheck)
1276 ASSERT(!storage->m_sparseValueMap);
1277
1278 unsigned numValuesInVector = 0;
1279 for (unsigned i = 0; i < m_vectorLength; ++i) {
1280 if (JSValue value = storage->m_vector[i]) {
1281 ASSERT(i < storage->m_length);
1282 if (type != DestructorConsistencyCheck)
1283 value.isUndefined(); // Likely to crash if the object was deallocated.
1284 ++numValuesInVector;
1285 } else {
1286 if (type == SortConsistencyCheck)
1287 ASSERT(i >= storage->m_numValuesInVector);
1288 }
1289 }
1290 ASSERT(numValuesInVector == storage->m_numValuesInVector);
1291 ASSERT(numValuesInVector <= storage->m_length);
1292
1293 if (storage->m_sparseValueMap) {
1294 SparseArrayValueMap::iterator end = storage->m_sparseValueMap->end();
1295 for (SparseArrayValueMap::iterator it = storage->m_sparseValueMap->begin(); it != end; ++it) {
1296 unsigned index = it->first;
1297 ASSERT(index < storage->m_length);
1298 ASSERT(index >= storage->m_vectorLength);
1299 ASSERT(index <= MAX_ARRAY_INDEX);
1300 ASSERT(it->second);
1301 if (type != DestructorConsistencyCheck)
1302 it->second.isUndefined(); // Likely to crash if the object was deallocated.
1303 }
1304 }
1305}
1306
1307#endif
1308
1309} // namespace JSC
Note: See TracBrowser for help on using the repository browser.