source: webkit/trunk/JavaScriptCore/kjs/JSArray.cpp@ 34867

Last change on this file since 34867 was 34867, checked in by Darin Adler, 17 years ago

2008-06-28 Darin Adler <Darin Adler>

Reviewed by Cameron.

SunSpider says 1.8% faster.

  • kjs/JSArray.cpp: (KJS::JSArray::JSArray): Initialize m_fastAccessCutoff when creating arrays. Also updated for new location of m_vectorLength. (KJS::JSArray::getItem): Updated for new location of m_vectorLength. (KJS::JSArray::getSlowCase): Added. Broke out the non-hot parts of getOwnPropertySlot to make the hot part faster. (KJS::JSArray::getOwnPropertySlot): Added a new faster case for indices lower than m_fastAccessCutoff. We can do theese with no additional checks or branches. (KJS::JSArray::put): Added a new faster case for indices lower than m_fastAccessCutoff. We can do theese with no additional checks or branches. Moved the maxArrayIndex handling out of this function. Added code to set m_fastAccessCutoff when the very last hole in an array is filled; this is how the cutoff gets set for most arrays. (KJS::JSArray::putSlowCase): Moved the rest of the put function logic in here, to make the hot part of the put function faster. (KJS::JSArray::deleteProperty): Added code to lower m_fastAccessCutoff when a delete makes a new hole in the array. (KJS::JSArray::getPropertyNames): Updated for new location of m_vectorLength. (KJS::JSArray::increaseVectorLength): Ditto. (KJS::JSArray::setLength): Added code to lower m_fastAccessCutoff when setLength makes the array smaller. (KJS::JSArray::mark): Updated for new location of m_vectorLength. (KJS::JSArray::sort): Ditto. Set m_fastAccessCutoff after moving all the holes to the end of the array. (KJS::JSArray::compactForSorting): Ditto. (KJS::JSArray::checkConsistency): Added consistency checks fro m_fastAccessCutoff and updated for the new location of m_vectorLength.
  • kjs/JSArray.h: Added declarations for slow case functions. Replaced m_vectorLength with m_fastAccessCutoff.
  • Property svn:eol-style set to native
File size: 28.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 "PropertyNameArray.h"
28#include <wtf/AVLTree.h>
29#include <wtf/Assertions.h>
30
31#define CHECK_ARRAY_CONSISTENCY 0
32
33using namespace std;
34
35namespace KJS {
36
37typedef HashMap<unsigned, JSValue*> SparseArrayValueMap;
38
39struct ArrayStorage {
40 unsigned m_vectorLength;
41 unsigned m_numValuesInVector;
42 SparseArrayValueMap* m_sparseValueMap;
43 void* lazyCreationData; // An JSArray subclass can use this to fill the vector lazily.
44 JSValue* m_vector[1];
45};
46
47// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
48static const unsigned maxArrayIndex = 0xFFFFFFFEU;
49
50// Our policy for when to use a vector and when to use a sparse map.
51// For all array indices under sparseArrayCutoff, we always use a vector.
52// When indices greater than sparseArrayCutoff are involved, we use a vector
53// as long as it is 1/8 full. If more sparse than that, we use a map.
54// This value has to be a macro to be used in max() and min() without introducing
55// a PIC branch in Mach-O binaries, see <rdar://problem/5971391>.
56#define sparseArrayCutoff 10000U
57static const unsigned minDensityMultiplier = 8;
58
59const ClassInfo JSArray::info = {"Array", 0, 0, 0};
60
61static inline size_t storageSize(unsigned vectorLength)
62{
63 return sizeof(ArrayStorage) - sizeof(JSValue*) + vectorLength * sizeof(JSValue*);
64}
65
66static inline unsigned increasedVectorLength(unsigned newLength)
67{
68 return (newLength * 3 + 1) / 2;
69}
70
71static inline bool isDenseEnoughForVector(unsigned length, unsigned numValues)
72{
73 return length / minDensityMultiplier <= numValues;
74}
75
76#if !CHECK_ARRAY_CONSISTENCY
77
78inline void JSArray::checkConsistency(ConsistencyCheckType)
79{
80}
81
82#endif
83
84JSArray::JSArray(JSObject* prototype, unsigned initialLength)
85 : JSObject(prototype)
86{
87 unsigned initialCapacity = min(initialLength, sparseArrayCutoff);
88
89 m_length = initialLength;
90 m_fastAccessCutoff = 0;
91 m_storage = static_cast<ArrayStorage*>(fastZeroedMalloc(storageSize(initialCapacity)));
92 m_storage->m_vectorLength = initialCapacity;
93
94 Heap::heap(this)->reportExtraMemoryCost(initialCapacity * sizeof(JSValue*));
95
96 checkConsistency();
97}
98
99JSArray::JSArray(JSObject* prototype, const ArgList& list)
100 : JSObject(prototype)
101{
102 unsigned length = list.size();
103
104 m_length = length;
105 m_fastAccessCutoff = length;
106
107 ArrayStorage* storage = static_cast<ArrayStorage*>(fastMalloc(storageSize(length)));
108
109 storage->m_vectorLength = length;
110 storage->m_numValuesInVector = length;
111 storage->m_sparseValueMap = 0;
112
113 size_t i = 0;
114 ArgList::const_iterator end = list.end();
115 for (ArgList::const_iterator it = list.begin(); it != end; ++it, ++i)
116 storage->m_vector[i] = *it;
117
118 m_storage = storage;
119
120 // When the array is created non-empty, its cells are filled, so it's really no worse than
121 // a property map. Therefore don't report extra memory cost.
122
123 checkConsistency();
124}
125
126JSArray::~JSArray()
127{
128 checkConsistency(DestructorConsistencyCheck);
129
130 delete m_storage->m_sparseValueMap;
131 fastFree(m_storage);
132}
133
134JSValue* JSArray::getItem(unsigned i) const
135{
136 ASSERT(i <= maxArrayIndex);
137
138 ArrayStorage* storage = m_storage;
139
140 if (i < storage->m_vectorLength) {
141 JSValue* value = storage->m_vector[i];
142 return value ? value : jsUndefined();
143 }
144
145 SparseArrayValueMap* map = storage->m_sparseValueMap;
146 if (!map)
147 return jsUndefined();
148
149 JSValue* value = map->get(i);
150 return value ? value : jsUndefined();
151}
152
153JSValue* JSArray::lengthGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
154{
155 return jsNumber(exec, static_cast<JSArray*>(slot.slotBase())->m_length);
156}
157
158NEVER_INLINE bool JSArray::getOwnPropertySlotSlowCase(ExecState* exec, unsigned i, PropertySlot& slot)
159{
160 ArrayStorage* storage = m_storage;
161
162 if (i >= m_length) {
163 if (i > maxArrayIndex)
164 return getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
165 return false;
166 }
167
168 if (i < storage->m_vectorLength) {
169 JSValue*& valueSlot = storage->m_vector[i];
170 if (valueSlot) {
171 slot.setValueSlot(&valueSlot);
172 return true;
173 }
174 } else if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
175 if (i >= sparseArrayCutoff) {
176 SparseArrayValueMap::iterator it = map->find(i);
177 if (it != map->end()) {
178 slot.setValueSlot(&it->second);
179 return true;
180 }
181 }
182 }
183
184 return false;
185}
186
187bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
188{
189 if (propertyName == exec->propertyNames().length) {
190 slot.setCustom(this, lengthGetter);
191 return true;
192 }
193
194 bool isArrayIndex;
195 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
196 if (isArrayIndex)
197 return JSArray::getOwnPropertySlot(exec, i, slot);
198
199 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
200}
201
202bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot)
203{
204 if (i < m_fastAccessCutoff) {
205 slot.setValueSlot(&m_storage->m_vector[i]);
206 return true;
207 }
208
209 return getOwnPropertySlotSlowCase(exec, i, slot);
210}
211
212// ECMA 15.4.5.1
213void JSArray::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
214{
215 bool isArrayIndex;
216 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
217 if (isArrayIndex) {
218 put(exec, i, value);
219 return;
220 }
221
222 if (propertyName == exec->propertyNames().length) {
223 unsigned newLength = value->toUInt32(exec);
224 if (value->toNumber(exec) != static_cast<double>(newLength)) {
225 throwError(exec, RangeError, "Invalid array length.");
226 return;
227 }
228 setLength(newLength);
229 return;
230 }
231
232 JSObject::put(exec, propertyName, value);
233}
234
235void JSArray::put(ExecState* exec, unsigned i, JSValue* value)
236{
237 checkConsistency();
238
239 if (i < m_fastAccessCutoff) {
240 m_storage->m_vector[i] = value;
241 checkConsistency();
242 return;
243 }
244
245 unsigned length = m_length;
246 if (i >= length && i <= maxArrayIndex) {
247 length = i + 1;
248 m_length = length;
249 }
250
251 if (i < m_storage->m_vectorLength) {
252 JSValue*& valueSlot = m_storage->m_vector[i];
253 if (valueSlot) {
254 valueSlot = value;
255 checkConsistency();
256 return;
257 }
258 valueSlot = value;
259 if (++m_storage->m_numValuesInVector == m_length)
260 m_fastAccessCutoff = m_length;
261 checkConsistency();
262 return;
263 }
264
265 putSlowCase(exec, i, value);
266}
267
268NEVER_INLINE void JSArray::putSlowCase(ExecState* exec, unsigned i, JSValue* value)
269{
270 ArrayStorage* storage = m_storage;
271 SparseArrayValueMap* map = storage->m_sparseValueMap;
272
273 if (i >= sparseArrayCutoff) {
274 if (i > maxArrayIndex) {
275 put(exec, Identifier::from(exec, i), value);
276 return;
277 }
278
279 // We miss some cases where we could compact the storage, such as a large array that is being filled from the end
280 // (which will only be compacted as we reach indices that are less than cutoff) - but this makes the check much faster.
281 if (!isDenseEnoughForVector(i + 1, storage->m_numValuesInVector + 1)) {
282 if (!map) {
283 map = new SparseArrayValueMap;
284 storage->m_sparseValueMap = map;
285 }
286 map->set(i, value);
287 return;
288 }
289 }
290
291 // We have decided that we'll put the new item into the vector.
292 // Fast case is when there is no sparse map, so we can increase the vector size without moving values from it.
293 if (!map || map->isEmpty()) {
294 increaseVectorLength(i + 1);
295 storage = m_storage;
296 ++storage->m_numValuesInVector;
297 storage->m_vector[i] = value;
298 checkConsistency();
299 return;
300 }
301
302 // Decide how many values it would be best to move from the map.
303 unsigned newNumValuesInVector = storage->m_numValuesInVector + 1;
304 unsigned newVectorLength = increasedVectorLength(i + 1);
305 for (unsigned j = max(storage->m_vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
306 newNumValuesInVector += map->contains(j);
307 if (i >= sparseArrayCutoff)
308 newNumValuesInVector -= map->contains(i);
309 if (isDenseEnoughForVector(newVectorLength, newNumValuesInVector)) {
310 unsigned proposedNewNumValuesInVector = newNumValuesInVector;
311 while (true) {
312 unsigned proposedNewVectorLength = increasedVectorLength(newVectorLength + 1);
313 for (unsigned j = max(newVectorLength, sparseArrayCutoff); j < proposedNewVectorLength; ++j)
314 proposedNewNumValuesInVector += map->contains(j);
315 if (!isDenseEnoughForVector(proposedNewVectorLength, proposedNewNumValuesInVector))
316 break;
317 newVectorLength = proposedNewVectorLength;
318 newNumValuesInVector = proposedNewNumValuesInVector;
319 }
320 }
321
322 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength)));
323
324 unsigned vectorLength = storage->m_vectorLength;
325 if (newNumValuesInVector == storage->m_numValuesInVector + 1) {
326 for (unsigned j = vectorLength; j < newVectorLength; ++j)
327 storage->m_vector[j] = 0;
328 if (i > sparseArrayCutoff)
329 map->remove(i);
330 } else {
331 for (unsigned j = vectorLength; j < max(vectorLength, sparseArrayCutoff); ++j)
332 storage->m_vector[j] = 0;
333 for (unsigned j = max(vectorLength, sparseArrayCutoff); j < newVectorLength; ++j)
334 storage->m_vector[j] = map->take(j);
335 }
336
337 storage->m_vector[i] = value;
338
339 storage->m_vectorLength = newVectorLength;
340 storage->m_numValuesInVector = newNumValuesInVector;
341
342 m_storage = storage;
343
344 checkConsistency();
345}
346
347bool JSArray::deleteProperty(ExecState* exec, const Identifier& propertyName)
348{
349 bool isArrayIndex;
350 unsigned i = propertyName.toArrayIndex(&isArrayIndex);
351 if (isArrayIndex)
352 return deleteProperty(exec, i);
353
354 if (propertyName == exec->propertyNames().length)
355 return false;
356
357 return JSObject::deleteProperty(exec, propertyName);
358}
359
360bool JSArray::deleteProperty(ExecState* exec, unsigned i)
361{
362 checkConsistency();
363
364 ArrayStorage* storage = m_storage;
365
366 if (i < storage->m_vectorLength) {
367 JSValue*& valueSlot = storage->m_vector[i];
368 if (!valueSlot) {
369 checkConsistency();
370 return false;
371 }
372 valueSlot = 0;
373 --storage->m_numValuesInVector;
374 if (m_fastAccessCutoff > i)
375 m_fastAccessCutoff = i;
376 checkConsistency();
377 return true;
378 }
379
380 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
381 if (i >= sparseArrayCutoff) {
382 SparseArrayValueMap::iterator it = map->find(i);
383 if (it != map->end()) {
384 map->remove(it);
385 checkConsistency();
386 return true;
387 }
388 }
389 }
390
391 checkConsistency();
392
393 if (i > maxArrayIndex)
394 return deleteProperty(exec, Identifier::from(exec, i));
395
396 return false;
397}
398
399void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
400{
401 // FIXME: Filling PropertyNameArray with an identifier for every integer
402 // is incredibly inefficient for large arrays. We need a different approach,
403 // which almost certainly means a different structure for PropertyNameArray.
404
405 ArrayStorage* storage = m_storage;
406
407 unsigned usedVectorLength = min(m_length, storage->m_vectorLength);
408 for (unsigned i = 0; i < usedVectorLength; ++i) {
409 if (storage->m_vector[i])
410 propertyNames.add(Identifier::from(exec, i));
411 }
412
413 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
414 SparseArrayValueMap::iterator end = map->end();
415 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
416 propertyNames.add(Identifier::from(exec, it->first));
417 }
418
419 JSObject::getPropertyNames(exec, propertyNames);
420}
421
422bool JSArray::increaseVectorLength(unsigned newLength)
423{
424 // This function leaves the array in an internally inconsistent state, because it does not move any values from sparse value map
425 // to the vector. Callers have to account for that, because they can do it more efficiently.
426
427 ArrayStorage* storage = m_storage;
428
429 unsigned vectorLength = storage->m_vectorLength;
430 ASSERT(newLength > vectorLength);
431 unsigned newVectorLength = increasedVectorLength(newLength);
432
433 storage = static_cast<ArrayStorage*>(fastRealloc(storage, storageSize(newVectorLength)));
434 if (!storage)
435 return false;
436
437 storage->m_vectorLength = newVectorLength;
438
439 for (unsigned i = vectorLength; i < newVectorLength; ++i)
440 storage->m_vector[i] = 0;
441
442 m_storage = storage;
443 return true;
444}
445
446void JSArray::setLength(unsigned newLength)
447{
448 checkConsistency();
449
450 ArrayStorage* storage = m_storage;
451
452 unsigned length = m_length;
453
454 if (newLength < length) {
455 if (m_fastAccessCutoff > newLength)
456 m_fastAccessCutoff = newLength;
457
458 unsigned usedVectorLength = min(length, storage->m_vectorLength);
459 for (unsigned i = newLength; i < usedVectorLength; ++i) {
460 JSValue*& valueSlot = storage->m_vector[i];
461 bool hadValue = valueSlot;
462 valueSlot = 0;
463 storage->m_numValuesInVector -= hadValue;
464 }
465
466 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
467 SparseArrayValueMap copy = *map;
468 SparseArrayValueMap::iterator end = copy.end();
469 for (SparseArrayValueMap::iterator it = copy.begin(); it != end; ++it) {
470 if (it->first >= newLength)
471 map->remove(it->first);
472 }
473 if (map->isEmpty()) {
474 delete map;
475 storage->m_sparseValueMap = 0;
476 }
477 }
478 }
479
480 m_length = newLength;
481
482 checkConsistency();
483}
484
485void JSArray::mark()
486{
487 JSObject::mark();
488
489 ArrayStorage* storage = m_storage;
490
491 unsigned usedVectorLength = min(m_length, storage->m_vectorLength);
492 for (unsigned i = 0; i < usedVectorLength; ++i) {
493 JSValue* value = storage->m_vector[i];
494 if (value && !value->marked())
495 value->mark();
496 }
497
498 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
499 SparseArrayValueMap::iterator end = map->end();
500 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
501 JSValue* value = it->second;
502 if (!value->marked())
503 value->mark();
504 }
505 }
506}
507
508typedef std::pair<JSValue*, UString> ArrayQSortPair;
509
510static int compareByStringPairForQSort(const void* a, const void* b)
511{
512 const ArrayQSortPair* va = static_cast<const ArrayQSortPair*>(a);
513 const ArrayQSortPair* vb = static_cast<const ArrayQSortPair*>(b);
514 return compare(va->second, vb->second);
515}
516
517void JSArray::sort(ExecState* exec)
518{
519 unsigned lengthNotIncludingUndefined = compactForSorting();
520 if (m_storage->m_sparseValueMap) {
521 exec->setException(Error::create(exec, GeneralError, "Out of memory"));
522 return;
523 }
524
525 if (!lengthNotIncludingUndefined)
526 return;
527
528 // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
529 // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
530 // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
531 // random or otherwise changing results, effectively making compare function inconsistent.
532
533 Vector<ArrayQSortPair> values(lengthNotIncludingUndefined);
534 if (!values.begin()) {
535 exec->setException(Error::create(exec, GeneralError, "Out of memory"));
536 return;
537 }
538
539 for (size_t i = 0; i < lengthNotIncludingUndefined; i++) {
540 JSValue* value = m_storage->m_vector[i];
541 ASSERT(!value->isUndefined());
542 values[i].first = value;
543 }
544
545 // FIXME: While calling these toString functions, the array could be mutated.
546 // In that case, objects pointed to by values in this vector might get garbage-collected!
547
548 // FIXME: The following loop continues to call toString on subsequent values even after
549 // a toString call raises an exception.
550
551 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
552 values[i].second = values[i].first->toString(exec);
553
554 if (exec->hadException())
555 return;
556
557 // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
558 // than O(N log N).
559
560#if HAVE(MERGESORT)
561 mergesort(values.begin(), values.size(), sizeof(ArrayQSortPair), compareByStringPairForQSort);
562#else
563 // FIXME: The qsort library function is likely to not be a stable sort.
564 // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
565 qsort(values.begin(), values.size(), sizeof(ArrayQSortPair), compareByStringPairForQSort);
566#endif
567
568 // FIXME: If the toString function changed the length of the array, this might be
569 // modifying the vector incorrectly.
570
571 for (size_t i = 0; i < lengthNotIncludingUndefined; i++)
572 m_storage->m_vector[i] = values[i].first;
573
574 checkConsistency(SortConsistencyCheck);
575}
576
577struct AVLTreeNodeForArrayCompare {
578 JSValue* value;
579
580 // Child pointers. The high bit of gt is robbed and used as the
581 // balance factor sign. The high bit of lt is robbed and used as
582 // the magnitude of the balance factor.
583 int32_t gt;
584 int32_t lt;
585};
586
587struct AVLTreeAbstractorForArrayCompare {
588 typedef int32_t handle; // Handle is an index into m_nodes vector.
589 typedef JSValue* key;
590 typedef int32_t size;
591
592 Vector<AVLTreeNodeForArrayCompare> m_nodes;
593 ExecState* m_exec;
594 JSValue* m_compareFunction;
595 CallType m_compareCallType;
596 const CallData* m_compareCallData;
597 JSValue* m_globalThisValue;
598
599 handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
600 void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
601 handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
602 void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
603
604 int get_balance_factor(handle h)
605 {
606 if (m_nodes[h].gt & 0x80000000)
607 return -1;
608 return static_cast<unsigned>(m_nodes[h].lt) >> 31;
609 }
610
611 void set_balance_factor(handle h, int bf)
612 {
613 if (bf == 0) {
614 m_nodes[h].lt &= 0x7FFFFFFF;
615 m_nodes[h].gt &= 0x7FFFFFFF;
616 } else {
617 m_nodes[h].lt |= 0x80000000;
618 if (bf < 0)
619 m_nodes[h].gt |= 0x80000000;
620 else
621 m_nodes[h].gt &= 0x7FFFFFFF;
622 }
623 }
624
625 int compare_key_key(key va, key vb)
626 {
627 ASSERT(!va->isUndefined());
628 ASSERT(!vb->isUndefined());
629
630 if (m_exec->hadException())
631 return 1;
632
633 ArgList arguments;
634 arguments.append(va);
635 arguments.append(vb);
636 double compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, m_globalThisValue, arguments)->toNumber(m_exec);
637 return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
638 }
639
640 int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
641 int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
642
643 static handle null() { return 0x7FFFFFFF; }
644};
645
646void JSArray::sort(ExecState* exec, JSValue* compareFunction, CallType callType, const CallData& callData)
647{
648 checkConsistency();
649
650 // FIXME: This ignores exceptions raised in the compare function or in toNumber.
651
652 // The maximum tree depth is compiled in - but the caller is clearly up to no good
653 // if a larger array is passed.
654 ASSERT(m_length <= static_cast<unsigned>(std::numeric_limits<int>::max()));
655 if (m_length > static_cast<unsigned>(std::numeric_limits<int>::max()))
656 return;
657
658 if (!m_length)
659 return;
660
661 unsigned usedVectorLength = min(m_length, m_storage->m_vectorLength);
662
663 AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
664 tree.abstractor().m_exec = exec;
665 tree.abstractor().m_compareFunction = compareFunction;
666 tree.abstractor().m_compareCallType = callType;
667 tree.abstractor().m_compareCallData = &callData;
668 tree.abstractor().m_globalThisValue = exec->globalThisValue();
669 tree.abstractor().m_nodes.resize(usedVectorLength + (m_storage->m_sparseValueMap ? m_storage->m_sparseValueMap->size() : 0));
670
671 if (!tree.abstractor().m_nodes.begin()) {
672 exec->setException(Error::create(exec, GeneralError, "Out of memory"));
673 return;
674 }
675
676 // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
677 // right out from under us while we're building the tree here.
678
679 unsigned numDefined = 0;
680 unsigned numUndefined = 0;
681
682 // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
683 for (; numDefined < usedVectorLength; ++numDefined) {
684 JSValue* v = m_storage->m_vector[numDefined];
685 if (!v || v->isUndefined())
686 break;
687 tree.abstractor().m_nodes[numDefined].value = v;
688 tree.insert(numDefined);
689 }
690 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
691 if (JSValue* v = m_storage->m_vector[i]) {
692 if (v->isUndefined())
693 ++numUndefined;
694 else {
695 tree.abstractor().m_nodes[numDefined].value = v;
696 tree.insert(numDefined);
697 ++numDefined;
698 }
699 }
700 }
701
702 unsigned newUsedVectorLength = numDefined + numUndefined;
703
704 if (SparseArrayValueMap* map = m_storage->m_sparseValueMap) {
705 newUsedVectorLength += map->size();
706 if (newUsedVectorLength > m_storage->m_vectorLength) {
707 if (!increaseVectorLength(newUsedVectorLength)) {
708 exec->setException(Error::create(exec, GeneralError, "Out of memory"));
709 return;
710 }
711 }
712
713 SparseArrayValueMap::iterator end = map->end();
714 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it) {
715 tree.abstractor().m_nodes[numDefined].value = it->second;
716 tree.insert(numDefined);
717 ++numDefined;
718 }
719
720 delete map;
721 m_storage->m_sparseValueMap = 0;
722 }
723
724 ASSERT(tree.abstractor().m_nodes.size() >= numDefined);
725
726 // FIXME: If the compare function changed the length of the array, the following might be
727 // modifying the vector incorrectly.
728
729 // Copy the values back into m_storage.
730 AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
731 iter.start_iter_least(tree);
732 for (unsigned i = 0; i < numDefined; ++i) {
733 m_storage->m_vector[i] = tree.abstractor().m_nodes[*iter].value;
734 ++iter;
735 }
736
737 // Put undefined values back in.
738 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
739 m_storage->m_vector[i] = jsUndefined();
740
741 // Ensure that unused values in the vector are zeroed out.
742 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
743 m_storage->m_vector[i] = 0;
744
745 m_fastAccessCutoff = newUsedVectorLength;
746 m_storage->m_numValuesInVector = newUsedVectorLength;
747
748 checkConsistency(SortConsistencyCheck);
749}
750
751unsigned JSArray::compactForSorting()
752{
753 checkConsistency();
754
755 ArrayStorage* storage = m_storage;
756
757 unsigned usedVectorLength = min(m_length, storage->m_vectorLength);
758
759 unsigned numDefined = 0;
760 unsigned numUndefined = 0;
761
762 for (; numDefined < usedVectorLength; ++numDefined) {
763 JSValue* v = storage->m_vector[numDefined];
764 if (!v || v->isUndefined())
765 break;
766 }
767 for (unsigned i = numDefined; i < usedVectorLength; ++i) {
768 if (JSValue* v = storage->m_vector[i]) {
769 if (v->isUndefined())
770 ++numUndefined;
771 else
772 storage->m_vector[numDefined++] = v;
773 }
774 }
775
776 unsigned newUsedVectorLength = numDefined + numUndefined;
777
778 if (SparseArrayValueMap* map = storage->m_sparseValueMap) {
779 newUsedVectorLength += map->size();
780 if (newUsedVectorLength > storage->m_vectorLength) {
781 if (!increaseVectorLength(newUsedVectorLength))
782 return 0;
783 storage = m_storage;
784 }
785
786 SparseArrayValueMap::iterator end = map->end();
787 for (SparseArrayValueMap::iterator it = map->begin(); it != end; ++it)
788 storage->m_vector[numDefined++] = it->second;
789
790 delete map;
791 storage->m_sparseValueMap = 0;
792 }
793
794 for (unsigned i = numDefined; i < newUsedVectorLength; ++i)
795 storage->m_vector[i] = jsUndefined();
796 for (unsigned i = newUsedVectorLength; i < usedVectorLength; ++i)
797 storage->m_vector[i] = 0;
798
799 m_fastAccessCutoff = newUsedVectorLength;
800 storage->m_numValuesInVector = newUsedVectorLength;
801
802 checkConsistency(SortConsistencyCheck);
803
804 return numDefined;
805}
806
807void* JSArray::lazyCreationData()
808{
809 return m_storage->lazyCreationData;
810}
811
812void JSArray::setLazyCreationData(void* d)
813{
814 m_storage->lazyCreationData = d;
815}
816
817#if CHECK_ARRAY_CONSISTENCY
818
819void JSArray::checkConsistency(ConsistencyCheckType type)
820{
821 ASSERT(m_storage);
822 if (type == SortConsistencyCheck)
823 ASSERT(!m_storage->m_sparseValueMap);
824
825 ASSERT(m_fastAccessCutoff <= m_length);
826 ASSERT(m_fastAccessCutoff <= m_storage->m_numValuesInVector);
827
828 unsigned numValuesInVector = 0;
829 for (unsigned i = 0; i < m_storage->m_vectorLength; ++i) {
830 if (JSValue* value = m_storage->m_vector[i]) {
831 ASSERT(i < m_length);
832 if (type != DestructorConsistencyCheck)
833 value->type(); // Likely to crash if the object was deallocated.
834 ++numValuesInVector;
835 } else {
836 ASSERT(i >= m_fastAccessCutoff);
837 if (type == SortConsistencyCheck)
838 ASSERT(i >= m_storage->m_numValuesInVector);
839 }
840 }
841 ASSERT(numValuesInVector == m_storage->m_numValuesInVector);
842
843 if (m_storage->m_sparseValueMap) {
844 SparseArrayValueMap::iterator end = m_storage->m_sparseValueMap->end();
845 for (SparseArrayValueMap::iterator it = m_storage->m_sparseValueMap->begin(); it != end; ++it) {
846 unsigned index = it->first;
847 ASSERT(index < m_length);
848 ASSERT(index >= m_storage->m_vectorLength);
849 ASSERT(index <= maxArrayIndex);
850 ASSERT(it->second);
851 if (type != DestructorConsistencyCheck)
852 it->second->type(); // Likely to crash if the object was deallocated.
853 }
854 }
855}
856
857#endif
858
859JSArray* constructEmptyArray(ExecState* exec)
860{
861 return new (exec) JSArray(exec->lexicalGlobalObject()->arrayPrototype(), 0);
862}
863
864JSArray* constructEmptyArray(ExecState* exec, unsigned initialLength)
865{
866 return new (exec) JSArray(exec->lexicalGlobalObject()->arrayPrototype(), initialLength);
867}
868
869JSArray* constructArray(ExecState* exec, JSValue* singleItemValue)
870{
871 ArgList values;
872 values.append(singleItemValue);
873 return new (exec) JSArray(exec->lexicalGlobalObject()->arrayPrototype(), values);
874}
875
876JSArray* constructArray(ExecState* exec, const ArgList& values)
877{
878 return new (exec) JSArray(exec->lexicalGlobalObject()->arrayPrototype(), values);
879}
880
881}
Note: See TracBrowser for help on using the repository browser.