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

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

2008-07-03 Geoffrey Garen <[email protected]>

Reviewed by Sam Weinig.


Removed checking for the array get/put fast case from the array code.
Callers who want the fast case should call getIndex and/or setIndex
instead. (get_by_val and put_by_val already do this.)


SunSpider reports no change overall, but a 1.4% speedup on fannkuch and
a 3.6% speedup on nsieve.

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