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

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

2008-06-27 Sam Weinig <[email protected]>

Rubber-stamped by Oliver Hunt.

Splits ArrayConstructor out of ArrayPrototype.h/cpp
Splits BooleanConstructor and BooleanPrototype out of BooleanObject.h/cpp

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