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

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

JavaScriptCore:

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

Reviewed by Oliver Hunt and Sam Weinig.

Next step toward putting doubles in registers: Prepare the Register class
and its clients for registers that don't contain JSValue*s.


This means a few things:


  1. Register::jsValue() clients, including ArgList clients, must now supply an ExecState* when accessing an entry in an ArgList, in case the entry will need to create a JSValue* on the fly.


  1. Register clients that definitely don't want to create a JSValue* on the fly now use different APIs: getJSValue() for clients that know the register contains a JSValue*, and v() for clients who just want a void*.


  1. I had to change some headers around in order to resolve dependency problems created by using a Register in the ArgList header.


SunSpider reports no change.

JavaScriptGlue:

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

Reviewed by Oliver Hunt and Sam Weinig.

Next step toward putting doubles in registers: Prepare the Register class
and its clients for registers that don't contain JSValue*s.

WebCore:

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

Reviewed by Oliver Hunt and Sam Weinig.

Next step toward putting doubles in registers: Prepare the Register class
and its clients for registers that don't contain JSValue*s.

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