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

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

2010-07-13 Andreas Kling <[email protected]>

Reviewed by Darin Adler.

Avoid slow-path for put() in Array.splice()
https://bugs.webkit.org/show_bug.cgi?id=41920

Defer creation of the returned array until its final size is known
to avoid growing it while adding elements.

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