source: webkit/trunk/JavaScriptCore/runtime/JSArray.cpp@ 47022

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

Stack overflow crash in JavaScript garbage collector mark pass
https://bugs.webkit.org/show_bug.cgi?id=12216

Reviewed by Gavin Barraclough and Sam Weinig

Make the GC mark phase iterative by using an explicit mark stack.
To do this marking any single object is performed in multiple stages

  • The object is appended to the MarkStack, this sets the marked bit for the object using the new markDirect() function, and then returns
  • When the MarkStack is drain()ed the object is popped off the stack and markChildren(MarkStack&) is called on the object to collect all of its children. drain() then repeats until the stack is empty.

Additionally I renamed a number of methods from 'mark' to 'markAggregate'
in order to make it more clear that marking of those object was not
going to result in an actual recursive mark.

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