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

Last change on this file since 52075 was 52047, checked in by [email protected], 15 years ago

Rolled out my last patch because the bots were crashing

  • Property svn:eol-style set to native
File size: 35.4 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
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
156 JSValue* vector = m_storage->m_vector;
157 for (size_t i = 0; i < initialCapacity; ++i)
158 vector[i] = JSValue();
159
160 checkConsistency();
161
162 Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue));
163}
164
165JSArray::JSArray(NonNullPassRefPtr<Structure> structure, const ArgList& list)
166 : JSObject(structure)
167{
168 unsigned initialCapacity = list.size();
169
170 m_storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(initialCapacity)));
171 m_storage->m_length = initialCapacity;
172 m_vectorLength = initialCapacity;
173 m_storage->m_numValuesInVector = initialCapacity;
174 m_storage->m_sparseValueMap = 0;
175
176 size_t i = 0;
177 ArgList::const_iterator end = list.end();
178 for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i)
179 m_storage->m_vector[i] = *it;
180
181 checkConsistency();
182
183 Heap::heap(this)->reportExtraMemoryCost(storageSize(initialCapacity));
184}
185
186JSArray::~JSArray()
187{
188 checkConsistency(DestructorConsistencyCheck);
189
190 delete m_storage->m_sparseValueMap;
191 fastFree(m_storage);
192}
193
194bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
195{
196 ArrayStorage* storage = m_storage;
197
198 if (i >= storage->m_length) {
199 if (i > MAX_ARRAY_INDEX)
200 return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
201 return false;
202 }
203
204 if (i < m_vectorLength) {
205 JSValue& valueSlot = storage->m_vector[i];
206 if (valueSlot) {
207 slot.setValueSlot(&valueSlot);
208 return true;
209 }
210 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
211 if (i >= MIN_SPARSE_ARRAY_INDEX) {
212 SparseArrayValueMap::iterator it = map->find(i);
213 if (it != map->end()) {
214 slot.setValueSlot(&it->second);
215 return true;
216 }
217 }
218 }
219
220 return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
221}
222
223bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
224{
225 if (propertyName == exec->propertyNames().length) {
226 slot.setValue(jsNumber(exec, length()));
227 return true;
228 }
229
230 bool isArrayIndex;
231 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
232 if (isArrayIndex)
233 return JSArray::getOwnPropertySlot(exec, i, slot);
234
235 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
236}
237
238bool JSArray::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
239{
240 if (propertyName == exec->propertyNames().length) {
241 descriptor.setDescriptor(jsNumber(exec, length()), DontDelete | DontEnum);
242 return true;
243 }
244
245 bool isArrayIndex;
246 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
247 if (isArrayIndex) {
248 if (i >= m_storage->m_length)
249 return false;
250 if (i < m_vectorLength) {
251 JSValue& value = m_storage->m_vector[i];
252 if (value) {
253 descriptor.setDescriptor(value, 0);
254 return true;
255 }
256 } else if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
257 if (i >= MIN_SPARSE_ARRAY_INDEX) {
258 SparseArrayValueMap::iterator it = map->find(i);
259 if (it != map->end()) {
260 descriptor.setDescriptor(it->second, 0);
261 return true;
262 }
263 }
264 }
265 }
266 return JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor);
267}
268
269// ECMA 15.4.5.1
270void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
271{
272 bool isArrayIndex;
273 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
274 if (isArrayIndex) {
275 put(exec, i, value);
276 return;
277 }
278
279 if (propertyName == exec->propertyNames().length) {
280 unsigned newLength = value.toUInt32(exec);
281 if (value.toNumber(exec) != static_cast<double>(newLength)) {
282 throwError(exec, RangeError, "Invalid array length.");
283 return;
284 }
285 setLength(newLength);
286 return;
287 }
288
289 JSObject::put(exec, propertyName, value, slot);
290}
291
292void JSArray::put(ExecState* exec, unsigned i, JSValue value)
293{
294 checkConsistency();
295
296 unsigned length = m_storage->m_length;
297 if (i >= length && i <= MAX_ARRAY_INDEX) {
298 length = i + 1;
299 m_storage->m_length = length;
300 }
301
302 if (i < m_vectorLength) {
303 JSValue& valueSlot = m_storage->m_vector[i];
304 if (valueSlot) {
305 valueSlot = value;
306 checkConsistency();
307 return;
308 }
309 valueSlot = value;
310 ++m_storage->m_numValuesInVector;
311 checkConsistency();
312 return;
313 }
314
315 putSlowCase(exec, i, value);
316}
317
318NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue value)
319{
320 ArrayStorage* storage = m_storage;
321 SparseArrayValueMap* map = storage->m_sparseValueMap;
322
323 if (i >= MIN_SPARSE_ARRAY_INDEX) {
324 if (i > MAX_ARRAY_INDEX) {
325 PutPropertySlot slot;
326 put(exec, Identifier::from(exec, i), value, slot);
327 return;
328 }
329
330 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
331 // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
332 if ((i > MAX_STORAGE_VECTOR_INDEX) || !isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
333 if (!map) {
334 map = new SparseArrayValueMap;
335 storage->m_sparseValueMap = map;
336 }
337 map->set(i, value);
338 return;
339 }
340 }
341
342 // We have decided that we'll put the new item into the vector.
343 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
344 if (!map || map->isEmpty()) {
345 if (increaseVectorLength(i + 1)) {
346 storage = m_storage;
347 storage->m_vector[i] = value;
348 ++storage->m_numValuesInVector;
349 checkConsistency();
350 } else
351 throwOutOfMemoryError(exec);
352 return;
353 }
354
355 // Decide how many values it would be best to move from the map.
356 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
357 unsigned newVectorLength = increasedVectorLength(i + 1);
358 for (unsigned j = max(m_vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
359 newNumValuesInVector += map->contains(j);
360 if (i >= MIN_SPARSE_ARRAY_INDEX)
361 newNumValuesInVector -= map->contains(i);
362 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
363 unsigned proposedNewNumValuesInVector = newNumValuesInVector;
364 // If newVectorLength is already the maximum - MAX_STORAGE_VECTOR_LENGTH - then do not attempt to grow any further.
365 while (newVectorLength < MAX_STORAGE_VECTOR_LENGTH) {
366 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
367 for (unsigned j = max(newVectorLength, MIN_SPARSE_ARRAY_INDEX); j < proposedNewVectorLength; ++j)
368 proposedNewNumValuesInVector += map->contains(j);
369 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
370 break;
371 newVectorLength = proposedNewVectorLength;
372 newNumValuesInVector = proposedNewNumValuesInVector;
373 }
374 }
375
376 if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage)) {
377 throwOutOfMemoryError(exec);
378 return;
379 }
380
381 unsigned vectorLength = m_vectorLength;
382
383 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
384
385 if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
386 for (unsigned j = vectorLength; j < newVectorLength; ++j)
387 storage->m_vector[j] = JSValue();
388 if (i > MIN_SPARSE_ARRAY_INDEX)
389 map->remove(i);
390 } else {
391 for (unsigned j = vectorLength; j < max(vectorLength, MIN_SPARSE_ARRAY_INDEX); ++j)
392 storage->m_vector[j] = JSValue();
393 for (unsigned j = max(vectorLength, MIN_SPARSE_ARRAY_INDEX); j < newVectorLength; ++j)
394 storage->m_vector[j] = map->take(j);
395 }
396
397 storage->m_vector[i] = value;
398
399 m_vectorLength = newVectorLength;
400 storage->m_numValuesInVector = newNumValuesInVector;
401
402 m_storage = storage;
403
404 checkConsistency();
405}
406
407bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName)
408{
409 bool isArrayIndex;
410 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
411 if (isArrayIndex)
412 return deleteProperty(exec, i);
413
414 if (propertyName == exec->propertyNames().length)
415 return false;
416
417 return JSObject::deleteProperty(exec, propertyName);
418}
419
420bool JSArray::deleteProperty(ExecState* exec, unsigned i)
421{
422 checkConsistency();
423
424 ArrayStorage* storage = m_storage;
425
426 if (i < m_vectorLength) {
427 JSValue& valueSlot = storage->m_vector[i];
428 if (!valueSlot) {
429 checkConsistency();
430 return false;
431 }
432 valueSlot = JSValue();
433 --storage->m_numValuesInVector;
434 checkConsistency();
435 return true;
436 }
437
438 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
439 if (i >= MIN_SPARSE_ARRAY_INDEX) {
440 SparseArrayValueMap::iterator it = map->find(i);
441 if (it != map->end()) {
442 map->remove(it);
443 checkConsistency();
444 return true;
445 }
446 }
447 }
448
449 checkConsistency();
450
451 if (i > MAX_ARRAY_INDEX)
452 return deleteProperty(exec, Identifier::from(exec, i));
453
454 return false;
455}
456
457void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
458{
459 // FIXME: Filling PropertyNameArray with an identifier for every integer
460 // is incredibly inefficient for large arrays. We need a different approach,
461 // which almost certainly means a different structure for PropertyNameArray.
462
463 ArrayStorage* storage = m_storage;
464
465 unsigned usedVectorLength = min(storage->m_length, m_vectorLength);
466 for (unsigned i = 0; i < usedVectorLength; ++i) {
467 if (storage->m_vector[i])
468 propertyNames.add(Identifier::from(exec, i));
469 }
470
471 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
472 SparseArrayValueMap::iterator end = map->end();
473 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
474 propertyNames.add(Identifier::from(exec, it->first));
475 }
476
477 JSObject::getOwnPropertyNames(exec, propertyNames);
478}
479
480bool JSArray::increaseVectorLength(unsigned newLength)
481{
482 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
483 // to the vector. Callers have to account for that, because they can do it more efficiently.
484
485 ArrayStorage* storage = m_storage;
486
487 unsigned vectorLength = m_vectorLength;
488 ASSERT(newLength > vectorLength);
489 ASSERT(newLength <= MAX_STORAGE_VECTOR_INDEX);
490 unsigned newVectorLength = increasedVectorLength(newLength);
491
492 if (!tryFastRealloc(storage, storageSize(newVectorLength)).getValue(storage))
493 return false;
494
495 Heap::heap(this)->reportExtraMemoryCost(storageSize(newVectorLength) - storageSize(vectorLength));
496 m_vectorLength = newVectorLength;
497
498 for (unsigned i = vectorLength; i < newVectorLength; ++i)
499 storage->m_vector[i] = JSValue();
500
501 m_storage = storage;
502 return true;
503}
504
505void JSArray::setLength(unsigned newLength)
506{
507 checkConsistency();
508
509 ArrayStorage* storage = m_storage;
510
511 unsigned length = m_storage->m_length;
512
513 if (newLength < length) {
514 unsigned usedVectorLength = min(length, m_vectorLength);
515 for (unsigned i = newLength; i < usedVectorLength; ++i) {
516 JSValue& valueSlot = storage->m_vector[i];
517 bool hadValue = valueSlot;
518 valueSlot = JSValue();
519 storage->m_numValuesInVector -= hadValue;
520 }
521
522 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
523 SparseArrayValueMap copy = *map;
524 SparseArrayValueMap::iterator end = copy.end();
525 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
526 if (it->first >= newLength)
527 map->remove(it->first);
528 }
529 if (map->isEmpty()) {
530 delete map;
531 storage->m_sparseValueMap = 0;
532 }
533 }
534 }
535
536 m_storage->m_length = newLength;
537
538 checkConsistency();
539}
540
541JSValue JSArray::pop()
542{
543 checkConsistency();
544
545 unsigned length = m_storage->m_length;
546 if (!length)
547 return jsUndefined();
548
549 --length;
550
551 JSValue result;
552
553 if (length < m_vectorLength) {
554 JSValue& valueSlot = m_storage->m_vector[length];
555 if (valueSlot) {
556 --m_storage->m_numValuesInVector;
557 result = valueSlot;
558 valueSlot = JSValue();
559 } else
560 result = jsUndefined();
561 } else {
562 result = jsUndefined();
563 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
564 SparseArrayValueMap::iterator it = map->find(length);
565 if (it != map->end()) {
566 result = it->second;
567 map->remove(it);
568 if (map->isEmpty()) {
569 delete map;
570 m_storage->m_sparseValueMap = 0;
571 }
572 }
573 }
574 }
575
576 m_storage->m_length = length;
577
578 checkConsistency();
579
580 return result;
581}
582
583void JSArray::push(ExecState* exec, JSValue value)
584{
585 checkConsistency();
586
587 if (m_storage->m_length < m_vectorLength) {
588 m_storage->m_vector[m_storage->m_length] = value;
589 ++m_storage->m_numValuesInVector;
590 ++m_storage->m_length;
591 checkConsistency();
592 return;
593 }
594
595 if (m_storage->m_length < MIN_SPARSE_ARRAY_INDEX) {
596 SparseArrayValueMap* map = m_storage->m_sparseValueMap;
597 if (!map || map->isEmpty()) {
598 if (increaseVectorLength(m_storage->m_length + 1)) {
599 m_storage->m_vector[m_storage->m_length] = value;
600 ++m_storage->m_numValuesInVector;
601 ++m_storage->m_length;
602 checkConsistency();
603 return;
604 }
605 checkConsistency();
606 throwOutOfMemoryError(exec);
607 return;
608 }
609 }
610
611 putSlowCase(exec, m_storage->m_length++, value);
612}
613
614void JSArray::markChildren(MarkStack& markStack)
615{
616 markChildrenDirect(markStack);
617}
618
619static int compareNumbersForQSort(const void* a, const void* b)
620{
621 double da = static_cast<const JSValue*>(a)->uncheckedGetNumber();
622 double db = static_cast<const JSValue*>(b)->uncheckedGetNumber();
623 return (da > db) - (da < db);
624}
625
626typedef std::pair<JSValue, UString> ValueStringPair;
627
628static int compareByStringPairForQSort(const void* a, const void* b)
629{
630 const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
631 const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
632 return compare(va->second, vb->second);
633}
634
635void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
636{
637 unsigned lengthNotIncludingUndefined = compactForSorting();
638 if (m_storage->m_sparseValueMap) {
639 throwOutOfMemoryError(exec);
640 return;
641 }
642
643 if (!lengthNotIncludingUndefined)
644 return;
645
646 bool allValuesAreNumbers = true;
647 size_t size = m_storage->m_numValuesInVector;
648 for (size_t i = 0; i < size; ++i) {
649 if (!m_storage->m_vector[i].isNumber()) {
650 allValuesAreNumbers = false;
651 break;
652 }
653 }
654
655 if (!allValuesAreNumbers)
656 return sort(exec, compareFunction, callType, callData);
657
658 // For numeric comparison, which is fast, qsort is faster than mergesort. We
659 // also don't require mergesort's stability, since there's no user visible
660 // side-effect from swapping the order of equal primitive values.
661 qsort(m_storage->m_vector, size, sizeof(JSValue), compareNumbersForQSort);
662
663 checkConsistency(SortConsistencyCheck);
664}
665
666void JSArray::sort(ExecState* exec)
667{
668 unsigned lengthNotIncludingUndefined = compactForSorting();
669 if (m_storage->m_sparseValueMap) {
670 throwOutOfMemoryError(exec);
671 return;
672 }
673
674 if (!lengthNotIncludingUndefined)
675 return;
676
677 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
678 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
679 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
680 // random or otherwise changing results, effectively making compare function inconsistent.
681
682 Vector<ValueStringPair> values(lengthNotIncludingUndefined);
683 if (!values.begin()) {
684 throwOutOfMemoryError(exec);
685 return;
686 }
687
688 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
689 JSValue value = m_storage->m_vector[i];
690 ASSERT(!value.isUndefined());
691 values[i].first = value;
692 }
693
694 // FIXME: While calling these toString functions, the array could be mutated.
695 // In that case, objects pointed to by values in this vector might get garbage-collected!
696
697 // FIXME: The following loop continues to call toString on subsequent values even after
698 // a toString call raises an exception.
699
700 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
701 values[i].second = values[i].first.toString(exec);
702
703 if (exec->hadException())
704 return;
705
706 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
707 // than O(N log N).
708
709#if HAVE(MERGESORT)
710 mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
711#else
712 // FIXME: The qsort library function is likely to not be a stable sort.
713 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
714 qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
715#endif
716
717 // FIXME: If the toString function changed the length of the array, this might be
718 // modifying the vector incorrectly.
719
720 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
721 m_storage->m_vector[i] = values[i].first;
722
723 checkConsistency(SortConsistencyCheck);
724}
725
726struct AVLTreeNodeForArrayCompare {
727 JSValue value;
728
729 // Child pointers. The high bit of gt is robbed and used as the
730 // balance factor sign. The high bit of lt is robbed and used as
731 // the magnitude of the balance factor.
732 int32_t gt;
733 int32_t lt;
734};
735
736struct AVLTreeAbstractorForArrayCompare {
737 typedef int32_t handle; // Handle is an index into m_nodes vector.
738 typedef JSValue key;
739 typedef int32_t size;
740
741 Vector<AVLTreeNodeForArrayCompare> m_nodes;
742 ExecState* m_exec;
743 JSValue m_compareFunction;
744 CallType m_compareCallType;
745 const CallData* m_compareCallData;
746 JSValue m_globalThisValue;
747 OwnPtr<CachedCall> m_cachedCall;
748
749 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
750 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
751 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
752 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
753
754 int get_balance_factor(handle h)
755 {
756 if (m_nodes[h].gt & 0x80000000)
757 return -1;
758 return static_cast<unsigned>(m_nodes[h].lt) >> 31;
759 }
760
761 void set_balance_factor(handle h, int bf)
762 {
763 if (bf == 0) {
764 m_nodes[h].lt &= 0x7FFFFFFF;
765 m_nodes[h].gt &= 0x7FFFFFFF;
766 } else {
767 m_nodes[h].lt |= 0x80000000;
768 if (bf < 0)
769 m_nodes[h].gt |= 0x80000000;
770 else
771 m_nodes[h].gt &= 0x7FFFFFFF;
772 }
773 }
774
775 int compare_key_key(key va, key vb)
776 {
777 ASSERT(!va.isUndefined());
778 ASSERT(!vb.isUndefined());
779
780 if (m_exec->hadException())
781 return 1;
782
783 double compareResult;
784 if (m_cachedCall) {
785 m_cachedCall->setThis(m_globalThisValue);
786 m_cachedCall->setArgument(0, va);
787 m_cachedCall->setArgument(1, vb);
788 compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec));
789 } else {
790 MarkedArgumentBuffer arguments;
791 arguments.append(va);
792 arguments.append(vb);
793 compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments).toNumber(m_exec);
794 }
795 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
796 }
797
798 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
799 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
800
801 static handle null() { return 0x7FFFFFFF; }
802};
803
804void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
805{
806 checkConsistency();
807
808 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
809
810 // The maximum tree depth is compiled in - but the caller is clearly up to no good
811 // if a larger array is passed.
812 ASSERT(m_storage->m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
813 if (m_storage->m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
814 return;
815
816 if (!m_storage->m_length)
817 return;
818
819 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
820
821 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
822 tree.abstractor().m_exec = exec;
823 tree.abstractor().m_compareFunction = compareFunction;
824 tree.abstractor().m_compareCallType = callType;
825 tree.abstractor().m_compareCallData = &callData;
826 tree.abstractor().m_globalThisValue = exec->globalThisValue();
827 tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0));
828
829 if (callType == CallTypeJS)
830 tree.abstractor().m_cachedCall.set(new CachedCall(exec, asFunction(compareFunction), 2, exec->exceptionSlot()));
831
832 if (!tree.abstractor().m_nodes.begin()) {
833 throwOutOfMemoryError(exec);
834 return;
835 }
836
837 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
838 // right out from under us while we're building the tree here.
839
840 unsigned numDefined = 0;
841 unsigned numUndefined = 0;
842
843 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
844 for (; numDefined < usedVectorLength; ++numDefined) {
845 JSValue v = m_storage->m_vector[numDefined];
846 if (!v || v.isUndefined())
847 break;
848 tree.abstractor().m_nodes[numDefined].value = v;
849 tree.insert(numDefined);
850 }
851 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
852 JSValue v = m_storage->m_vector[i];
853 if (v) {
854 if (v.isUndefined())
855 ++numUndefined;
856 else {
857 tree.abstractor().m_nodes[numDefined].value = v;
858 tree.insert(numDefined);
859 ++numDefined;
860 }
861 }
862 }
863
864 unsigned newUsedVectorLength = numDefined + numUndefined;
865
866 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
867 newUsedVectorLength += map->size();
868 if (newUsedVectorLength > m_vectorLength) {
869 // Check that it is possible to allocate an array large enough to hold all the entries.
870 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength)) {
871 throwOutOfMemoryError(exec);
872 return;
873 }
874 }
875
876 SparseArrayValueMap::iterator end = map->end();
877 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
878 tree.abstractor().m_nodes[numDefined].value = it->second;
879 tree.insert(numDefined);
880 ++numDefined;
881 }
882
883 delete map;
884 m_storage->m_sparseValueMap = 0;
885 }
886
887 ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
888
889 // FIXME: If the compare function changed the length of the array, the following might be
890 // modifying the vector incorrectly.
891
892 // Copy the values back into m_storage.
893 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
894 iter.start_iter_least(tree);
895 for (unsigned i = 0; i < numDefined; ++i) {
896 m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value;
897 ++iter;
898 }
899
900 // Put undefined values back in.
901 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
902 m_storage->m_vector[i] = jsUndefined();
903
904 // Ensure that unused values in the vector are zeroed out.
905 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
906 m_storage->m_vector[i] = JSValue();
907
908 m_storage->m_numValuesInVector = newUsedVectorLength;
909
910 checkConsistency(SortConsistencyCheck);
911}
912
913void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
914{
915 JSValue* vector = m_storage->m_vector;
916 unsigned vectorEnd = min(m_storage->m_length, m_vectorLength);
917 unsigned i = 0;
918 for (; i < vectorEnd; ++i) {
919 JSValue& v = vector[i];
920 if (!v)
921 break;
922 args.append(v);
923 }
924
925 for (; i < m_storage->m_length; ++i)
926 args.append(get(exec, i));
927}
928
929void JSArray::copyToRegisters(ExecState* exec, Register* buffer, uint32_t maxSize)
930{
931 ASSERT(m_storage->m_length == maxSize);
932 UNUSED_PARAM(maxSize);
933 JSValue* vector = m_storage->m_vector;
934 unsigned vectorEnd = min(m_storage->m_length, m_vectorLength);
935 unsigned i = 0;
936 for (; i < vectorEnd; ++i) {
937 JSValue& v = vector[i];
938 if (!v)
939 break;
940 buffer[i] = v;
941 }
942
943 for (; i < m_storage->m_length; ++i)
944 buffer[i] = get(exec, i);
945}
946
947unsigned JSArray::compactForSorting()
948{
949 checkConsistency();
950
951 ArrayStorage* storage = m_storage;
952
953 unsigned usedVectorLength = min(m_storage->m_length, m_vectorLength);
954
955 unsigned numDefined = 0;
956 unsigned numUndefined = 0;
957
958 for (; numDefined < usedVectorLength; ++numDefined) {
959 JSValue v = storage->m_vector[numDefined];
960 if (!v || v.isUndefined())
961 break;
962 }
963 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
964 JSValue v = storage->m_vector[i];
965 if (v) {
966 if (v.isUndefined())
967 ++numUndefined;
968 else
969 storage->m_vector[numDefined++] = v;
970 }
971 }
972
973 unsigned newUsedVectorLength = numDefined + numUndefined;
974
975 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
976 newUsedVectorLength += map->size();
977 if (newUsedVectorLength > m_vectorLength) {
978 // Check that it is possible to allocate an array large enough to hold all the entries - if not,
979 // exception is thrown by caller.
980 if ((newUsedVectorLength > MAX_STORAGE_VECTOR_LENGTH) || !increaseVectorLength(newUsedVectorLength))
981 return 0;
982 storage = m_storage;
983 }
984
985 SparseArrayValueMap::iterator end = map->end();
986 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
987 storage->m_vector[numDefined++] = it->second;
988
989 delete map;
990 storage->m_sparseValueMap = 0;
991 }
992
993 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
994 storage->m_vector[i] = jsUndefined();
995 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
996 storage->m_vector[i] = JSValue();
997
998 storage->m_numValuesInVector = newUsedVectorLength;
999
1000 checkConsistency(SortConsistencyCheck);
1001
1002 return numDefined;
1003}
1004
1005void* JSArray::lazyCreationData()
1006{
1007 return m_storage->lazyCreationData;
1008}
1009
1010void JSArray::setLazyCreationData(void* d)
1011{
1012 m_storage->lazyCreationData = d;
1013}
1014
1015#if CHECK_ARRAY_CONSISTENCY
1016
1017void JSArray::checkConsistency(ConsistencyCheckType type)
1018{
1019 ASSERT(m_storage);
1020 if (type == SortConsistencyCheck)
1021 ASSERT(!m_storage->m_sparseValueMap);
1022
1023 unsigned numValuesInVector = 0;
1024 for (unsigned i = 0; i < m_vectorLength; ++i) {
1025 if (JSValue value = m_storage->m_vector[i]) {
1026 ASSERT(i < m_storage->m_length);
1027 if (type != DestructorConsistencyCheck)
1028 value->type(); // Likely to crash if the object was deallocated.
1029 ++numValuesInVector;
1030 } else {
1031 if (type == SortConsistencyCheck)
1032 ASSERT(i >= m_storage->m_numValuesInVector);
1033 }
1034 }
1035 ASSERT(numValuesInVector == m_storage->m_numValuesInVector);
1036 ASSERT(numValuesInVector <= m_storage->m_length);
1037
1038 if (m_storage->m_sparseValueMap) {
1039 SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end();
1040 for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) {
1041 unsigned index = it->first;
1042 ASSERT(index < m_storage->m_length);
1043 ASSERT(index >= m_vectorLength);
1044 ASSERT(index <= MAX_ARRAY_INDEX);
1045 ASSERT(it->second);
1046 if (type != DestructorConsistencyCheck)
1047 it->second->type(); // Likely to crash if the object was deallocated.
1048 }
1049 }
1050}
1051
1052#endif
1053
1054} // namespace JSC
Note: See TracBrowser for help on using the repository browser.