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

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

JavaScriptCore:

2008-08-17 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Made room for a free word in JSCell.


SunSpider says no change.


I changed JSCallbackObjectData, Arguments, JSArray, and RegExpObject to
store auxiliary data in a secondary structure.

I changed InternalFunction to store the function's name in the property
map.


I changed JSGlobalObjectData to use a virtual destructor, so WebCore's
JSDOMWindowBaseData could inherit from it safely. (It's a strange design
for JSDOMWindowBase to allocate an object that JSGlobalObject deletes,
but that's really our only option, given the size constraint.)


I also added a bunch of compile-time ASSERTs, and removed lots of comments
in JSObject.h because they were often out of date, and they got in the
way of reading what was actually going on.


Also renamed JSArray::getLength to JSArray::length, to match our style
guidelines.

WebCore:

2008-08-17 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Made room for a free word in JSCell.


Changed JSDOMWindowBase to store its auxiliary data in a subclass of
JSGlobalData, so the two could share a pointer.


Added a bunch of ASSERTs, to help catch over-sized objects.

WebKit/mac:

2008-08-17 Geoffrey Garen <[email protected]>

Reviewed by Cameron Zwarich.

Made room for a free word in JSCell.


(Updated for JavaScriptCore changes.)

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