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

Last change on this file since 45011 was 43661, checked in by Darin Adler, 16 years ago

JavaScriptCore:

2009-05-13 Darin Adler <Darin Adler>

Revert the parser arena change. It was a slowdown, not a speedup.
Better luck next time (I'll break it up into pieces).

WebCore:

2009-05-13 Darin Adler <Darin Adler>

Revert the parser arena change. It was a slowdown, not a speedup.
Better luck next time (I'll break it up into pieces).

WebKit/mac:

2009-05-13 Darin Adler <Darin Adler>

Revert the parser arena change. It was a slowdown, not a speedup.
Better luck next time (I'll break it up into pieces).

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