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

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

2010-01-11 Geoffrey Garen <[email protected]>

Reviewed by Alexey Proskuryakov.

https://bugs.webkit.org/show_bug.cgi?id=33481
Uninitialized data members in ArrayStorage


SunSpider reports no change.

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