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