2
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3
* Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
4
* Copyright (C) 2003 Peter Kelly (pmk@post.com)
5
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
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.
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.
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
26
#include "ArrayPrototype.h"
27
#include "ButterflyInlines.h"
28
#include "CachedCall.h"
29
#include "CopiedSpace.h"
30
#include "CopiedSpaceInlines.h"
32
#include "Executable.h"
33
#include "GetterSetter.h"
34
#include "IndexingHeaderInlines.h"
35
#include "PropertyNameArray.h"
37
#include <wtf/AVLTree.h>
38
#include <wtf/Assertions.h>
39
#include <wtf/OwnPtr.h>
40
#include <Operations.h>
47
ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
49
const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
51
Butterfly* createArrayButterflyInDictionaryIndexingMode(JSGlobalData& globalData, unsigned initialLength)
53
Butterfly* butterfly = Butterfly::create(
54
globalData, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
55
ArrayStorage* storage = butterfly->arrayStorage();
56
storage->setLength(initialLength);
57
storage->setVectorLength(0);
58
storage->m_indexBias = 0;
59
storage->m_sparseMap.clear();
60
storage->m_numValuesInVector = 0;
64
void JSArray::setLengthWritable(ExecState* exec, bool writable)
66
ASSERT(isLengthWritable() || !writable);
67
if (!isLengthWritable() || writable)
70
enterDictionaryIndexingMode(exec->globalData());
72
SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
74
map->setLengthIsReadOnly();
77
// Defined in ES5.1 15.4.5.1
78
bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
80
JSArray* array = jsCast<JSArray*>(object);
82
// 3. If P is "length", then
83
if (propertyName == exec->propertyNames().length) {
84
// All paths through length definition call the default [[DefineOwnProperty]], hence:
85
// from ES5.1 8.12.9 7.a.
86
if (descriptor.configurablePresent() && descriptor.configurable())
87
return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
88
// from ES5.1 8.12.9 7.b.
89
if (descriptor.enumerablePresent() && descriptor.enumerable())
90
return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
92
// a. If the [[Value]] field of Desc is absent, then
93
// a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
94
if (descriptor.isAccessorDescriptor())
95
return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
96
// from ES5.1 8.12.9 10.a.
97
if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
98
return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
99
// This descriptor is either just making length read-only, or changing nothing!
100
if (!descriptor.value()) {
101
if (descriptor.writablePresent())
102
array->setLengthWritable(exec, descriptor.writable());
106
// b. Let newLenDesc be a copy of Desc.
107
// c. Let newLen be ToUint32(Desc.[[Value]]).
108
unsigned newLen = descriptor.value().toUInt32(exec);
109
// d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
110
if (newLen != descriptor.value().toNumber(exec)) {
111
throwError(exec, createRangeError(exec, "Invalid array length"));
115
// Based on SameValue check in 8.12.9, this is always okay.
116
if (newLen == array->length()) {
117
if (descriptor.writablePresent())
118
array->setLengthWritable(exec, descriptor.writable());
122
// e. Set newLenDesc.[[Value] to newLen.
123
// f. If newLen >= oldLen, then
124
// f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
125
// g. Reject if oldLenDesc.[[Writable]] is false.
126
if (!array->isLengthWritable())
127
return reject(exec, throwException, "Attempting to change value of a readonly property.");
129
// h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
131
// i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
132
// i.ii. Let newWritable be false.
133
// i.iii. Set newLenDesc.[[Writable] to true.
134
// j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
135
// k. If succeeded is false, return false.
136
// l. While newLen < oldLen repeat,
137
// l.i. Set oldLen to oldLen ā 1.
138
// l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
139
// l.iii. If deleteSucceeded is false, then
140
if (!array->setLength(exec, newLen, throwException)) {
141
// 1. Set newLenDesc.[[Value] to oldLen+1.
142
// 2. If newWritable is false, set newLenDesc.[[Writable] to false.
143
// 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
145
if (descriptor.writablePresent())
146
array->setLengthWritable(exec, descriptor.writable());
150
// m. If newWritable is false, then
151
// i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
152
// Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
154
if (descriptor.writablePresent())
155
array->setLengthWritable(exec, descriptor.writable());
160
// 4. Else if P is an array index (15.4), then
161
// a. Let index be ToUint32(P).
162
unsigned index = propertyName.asIndex();
163
if (index != PropertyName::NotAnIndex) {
164
// b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
165
if (index >= array->length() && !array->isLengthWritable())
166
return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property.");
167
// c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
168
// d. Reject if succeeded is false.
169
// e. If index >= oldLen
170
// e.i. Set oldLenDesc.[[Value]] to index + 1.
171
// e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
173
return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
176
return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
179
bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
181
JSArray* thisObject = jsCast<JSArray*>(cell);
182
if (propertyName == exec->propertyNames().length) {
183
slot.setValue(jsNumber(thisObject->length()));
187
return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
190
bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
192
JSArray* thisObject = jsCast<JSArray*>(object);
193
if (propertyName == exec->propertyNames().length) {
194
descriptor.setDescriptor(jsNumber(thisObject->length()), thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
198
return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
202
void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
204
JSArray* thisObject = jsCast<JSArray*>(cell);
206
if (propertyName == exec->propertyNames().length) {
207
unsigned newLength = value.toUInt32(exec);
208
if (value.toNumber(exec) != static_cast<double>(newLength)) {
209
throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
212
thisObject->setLength(exec, newLength, slot.isStrictMode());
216
JSObject::put(thisObject, exec, propertyName, value, slot);
219
bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
221
JSArray* thisObject = jsCast<JSArray*>(cell);
223
if (propertyName == exec->propertyNames().length)
226
return JSObject::deleteProperty(thisObject, exec, propertyName);
229
static int compareKeysForQSort(const void* a, const void* b)
231
unsigned da = *static_cast<const unsigned*>(a);
232
unsigned db = *static_cast<const unsigned*>(b);
233
return (da > db) - (da < db);
236
void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
238
JSArray* thisObject = jsCast<JSArray*>(object);
240
if (mode == IncludeDontEnumProperties)
241
propertyNames.add(exec->propertyNames().length);
243
JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
246
// This method makes room in the vector, but leaves the new space for count slots uncleared.
247
bool JSArray::unshiftCountSlowCase(JSGlobalData& globalData, bool addToFront, unsigned count)
249
ArrayStorage* storage = ensureArrayStorage(globalData);
250
Butterfly* butterfly = storage->butterfly();
251
unsigned propertyCapacity = structure()->outOfLineCapacity();
252
unsigned propertySize = structure()->outOfLineSize();
254
// If not, we should have handled this on the fast path.
255
ASSERT(!addToFront || count > storage->m_indexBias);
258
// Gather 4 key metrics:
259
// * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
260
// * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
261
// * currentCapacity - what is the current size of the vector, including any pre-capacity.
262
// * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
264
unsigned length = storage->length();
265
unsigned usedVectorLength = min(storage->vectorLength(), length);
266
ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
267
// Check that required vector length is possible, in an overflow-safe fashion.
268
if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
270
unsigned requiredVectorLength = usedVectorLength + count;
271
ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
272
// The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
273
ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
274
unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
275
// The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
276
unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1);
279
// We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
281
void* newAllocBase = 0;
282
unsigned newStorageCapacity;
283
// If the current storage array is sufficiently large (but not too large!) then just keep using it.
284
if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
285
newAllocBase = butterfly->base(structure());
286
newStorageCapacity = currentCapacity;
288
size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
289
if (!globalData.heap.tryAllocateStorage(newSize, &newAllocBase))
291
newStorageCapacity = desiredCapacity;
295
// Work out where we're going to move things to.
297
// Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
298
// If we're adding to the end, we'll add all the new space to the end.
299
// If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
300
// If it did, we calculate the amount that will remain based on an atomic decay - leave the
301
// vector with half the post-capacity it had previously.
302
unsigned postCapacity = 0;
304
postCapacity = max(newStorageCapacity - requiredVectorLength, count);
305
else if (length < storage->vectorLength()) {
306
// Atomic decay, + the post-capacity cannot be greater than what is available.
307
postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
308
// If we're moving contents within the same allocation, the post-capacity is being reduced.
309
ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length);
312
unsigned newVectorLength = requiredVectorLength + postCapacity;
313
unsigned newIndexBias = newStorageCapacity - newVectorLength;
315
Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity);
318
ASSERT(count + usedVectorLength <= newVectorLength);
319
memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
320
memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
321
} else if ((newAllocBase != butterfly->base(structure())) || (newIndexBias != storage->m_indexBias)) {
322
memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
323
memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
325
WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector;
326
for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
327
newVector[i].clear();
330
newButterfly->arrayStorage()->setVectorLength(newVectorLength);
331
newButterfly->arrayStorage()->m_indexBias = newIndexBias;
333
m_butterfly = newButterfly;
338
bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
340
unsigned length = storage->length();
342
// If the length is read only then we enter sparse mode, so should enter the following 'if'.
343
ASSERT(isLengthWritable() || storage->m_sparseMap);
345
if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
346
// Fail if the length is not writable.
347
if (map->lengthIsReadOnly())
348
return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
350
if (newLength < length) {
351
// Copy any keys we might be interested in into a vector.
352
Vector<unsigned> keys;
353
keys.reserveCapacity(min(map->size(), static_cast<size_t>(length - newLength)));
354
SparseArrayValueMap::const_iterator end = map->end();
355
for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
356
unsigned index = static_cast<unsigned>(it->key);
357
if (index < length && index >= newLength)
361
// Check if the array is in sparse mode. If so there may be non-configurable
362
// properties, so we have to perform deletion with caution, if not we can
363
// delete values in any order.
364
if (map->sparseMode()) {
365
qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
366
unsigned i = keys.size();
368
unsigned index = keys[--i];
369
SparseArrayValueMap::iterator it = map->find(index);
370
ASSERT(it != map->notFound());
371
if (it->value.attributes & DontDelete) {
372
storage->setLength(index + 1);
373
return reject(exec, throwException, "Unable to delete property.");
378
for (unsigned i = 0; i < keys.size(); ++i)
379
map->remove(keys[i]);
381
deallocateSparseIndexMap();
386
if (newLength < length) {
387
// Delete properties from the vector.
388
unsigned usedVectorLength = min(length, storage->vectorLength());
389
for (unsigned i = newLength; i < usedVectorLength; ++i) {
390
WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
391
bool hadValue = valueSlot;
393
storage->m_numValuesInVector -= hadValue;
397
storage->setLength(newLength);
402
bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
404
switch (structure()->indexingType()) {
408
if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
409
return setLengthWithArrayStorage(
410
exec, newLength, throwException,
411
convertContiguousToArrayStorage(exec->globalData()));
413
createInitialUndecided(exec->globalData(), newLength);
416
case ArrayWithUndecided:
418
case ArrayWithDouble:
419
case ArrayWithContiguous:
420
if (newLength == m_butterfly->publicLength())
422
if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push.
423
|| (newLength >= MIN_SPARSE_ARRAY_INDEX
424
&& !isDenseEnoughForVector(newLength, countElements()))) {
425
return setLengthWithArrayStorage(
426
exec, newLength, throwException,
427
ensureArrayStorage(exec->globalData()));
429
if (newLength > m_butterfly->publicLength()) {
430
ensureLength(exec->globalData(), newLength);
433
if (structure()->indexingType() == ArrayWithDouble) {
434
for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
435
m_butterfly->contiguousDouble()[i] = QNaN;
437
for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
438
m_butterfly->contiguous()[i].clear();
440
m_butterfly->setPublicLength(newLength);
443
case ArrayWithArrayStorage:
444
case ArrayWithSlowPutArrayStorage:
445
return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
453
JSValue JSArray::pop(ExecState* exec)
455
switch (structure()->indexingType()) {
457
return jsUndefined();
459
case ArrayWithUndecided:
460
if (!m_butterfly->publicLength())
461
return jsUndefined();
462
// We have nothing but holes. So, drop down to the slow version.
466
case ArrayWithContiguous: {
467
unsigned length = m_butterfly->publicLength();
470
return jsUndefined();
472
ASSERT(length < m_butterfly->vectorLength());
473
JSValue value = m_butterfly->contiguous()[length].get();
475
m_butterfly->contiguous()[length].clear();
476
m_butterfly->setPublicLength(length);
482
case ArrayWithDouble: {
483
unsigned length = m_butterfly->publicLength();
486
return jsUndefined();
488
ASSERT(length < m_butterfly->vectorLength());
489
double value = m_butterfly->contiguousDouble()[length];
490
if (value == value) {
491
m_butterfly->contiguousDouble()[length] = QNaN;
492
m_butterfly->setPublicLength(length);
493
return JSValue(JSValue::EncodeAsDouble, value);
498
case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
499
ArrayStorage* storage = m_butterfly->arrayStorage();
501
unsigned length = storage->length();
503
if (!isLengthWritable())
504
throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
505
return jsUndefined();
508
unsigned index = length - 1;
509
if (index < storage->vectorLength()) {
510
WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
512
--storage->m_numValuesInVector;
513
JSValue element = valueSlot.get();
516
ASSERT(isLengthWritable());
517
storage->setLength(index);
529
unsigned index = getArrayLength() - 1;
530
// Let element be the result of calling the [[Get]] internal method of O with argument indx.
531
JSValue element = get(exec, index);
532
if (exec->hadException())
533
return jsUndefined();
534
// Call the [[Delete]] internal method of O with arguments indx and true.
535
if (!deletePropertyByIndex(this, exec, index)) {
536
throwTypeError(exec, "Unable to delete property.");
537
return jsUndefined();
539
// Call the [[Put]] internal method of O with arguments "length", indx, and true.
540
setLength(exec, index, true);
545
// Push & putIndex are almost identical, with two small differences.
546
// - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
547
// - pushing to an array of length 2^32-1 stores the property, but throws a range error.
548
void JSArray::push(ExecState* exec, JSValue value)
550
switch (structure()->indexingType()) {
552
createInitialUndecided(exec->globalData(), 0);
556
case ArrayWithUndecided: {
557
convertUndecidedForValue(exec->globalData(), value);
562
case ArrayWithInt32: {
563
if (!value.isInt32()) {
564
convertInt32ForValue(exec->globalData(), value);
569
unsigned length = m_butterfly->publicLength();
570
ASSERT(length <= m_butterfly->vectorLength());
571
if (length < m_butterfly->vectorLength()) {
572
m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
573
m_butterfly->setPublicLength(length + 1);
577
if (length > MAX_ARRAY_INDEX) {
578
methodTable()->putByIndex(this, exec, length, value, true);
579
if (!exec->hadException())
580
throwError(exec, createRangeError(exec, "Invalid array length"));
584
putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
588
case ArrayWithContiguous: {
589
unsigned length = m_butterfly->publicLength();
590
ASSERT(length <= m_butterfly->vectorLength());
591
if (length < m_butterfly->vectorLength()) {
592
m_butterfly->contiguous()[length].set(exec->globalData(), this, value);
593
m_butterfly->setPublicLength(length + 1);
597
if (length > MAX_ARRAY_INDEX) {
598
methodTable()->putByIndex(this, exec, length, value, true);
599
if (!exec->hadException())
600
throwError(exec, createRangeError(exec, "Invalid array length"));
604
putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
608
case ArrayWithDouble: {
609
if (!value.isNumber()) {
610
convertDoubleToContiguous(exec->globalData());
614
double valueAsDouble = value.asNumber();
615
if (valueAsDouble != valueAsDouble) {
616
convertDoubleToContiguous(exec->globalData());
621
unsigned length = m_butterfly->publicLength();
622
ASSERT(length <= m_butterfly->vectorLength());
623
if (length < m_butterfly->vectorLength()) {
624
m_butterfly->contiguousDouble()[length] = valueAsDouble;
625
m_butterfly->setPublicLength(length + 1);
629
if (length > MAX_ARRAY_INDEX) {
630
methodTable()->putByIndex(this, exec, length, value, true);
631
if (!exec->hadException())
632
throwError(exec, createRangeError(exec, "Invalid array length"));
636
putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
640
case ArrayWithSlowPutArrayStorage: {
641
unsigned oldLength = length();
642
if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) {
643
if (!exec->hadException() && oldLength < 0xFFFFFFFFu)
644
setLength(exec, oldLength + 1, true);
650
case ArrayWithArrayStorage: {
651
ArrayStorage* storage = m_butterfly->arrayStorage();
653
// Fast case - push within vector, always update m_length & m_numValuesInVector.
654
unsigned length = storage->length();
655
if (length < storage->vectorLength()) {
656
storage->m_vector[length].set(exec->globalData(), this, value);
657
storage->setLength(length + 1);
658
++storage->m_numValuesInVector;
662
// Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
663
if (storage->length() > MAX_ARRAY_INDEX) {
664
methodTable()->putByIndex(this, exec, storage->length(), value, true);
665
// Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
666
if (!exec->hadException())
667
throwError(exec, createRangeError(exec, "Invalid array length"));
671
// Handled the same as putIndex.
672
putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
677
ASSERT_NOT_REACHED();
681
bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage* storage)
683
unsigned oldLength = storage->length();
684
ASSERT(count <= oldLength);
686
// If the array contains holes or is otherwise in an abnormal state,
687
// use the generic algorithm in ArrayPrototype.
688
if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode() || shouldUseSlowPut(structure()->indexingType()))
694
unsigned length = oldLength - count;
696
storage->m_numValuesInVector -= count;
697
storage->setLength(length);
699
unsigned vectorLength = storage->vectorLength();
703
if (startIndex >= vectorLength)
706
if (startIndex + count > vectorLength)
707
count = vectorLength - startIndex;
709
unsigned usedVectorLength = min(vectorLength, oldLength);
711
vectorLength -= count;
712
storage->setVectorLength(vectorLength);
715
if (startIndex < usedVectorLength - (startIndex + count)) {
718
storage->m_vector + count,
720
sizeof(JSValue) * startIndex);
722
m_butterfly = m_butterfly->shift(structure(), count);
723
storage = m_butterfly->arrayStorage();
724
storage->m_indexBias += count;
727
storage->m_vector + startIndex,
728
storage->m_vector + startIndex + count,
729
sizeof(JSValue) * (usedVectorLength - (startIndex + count)));
730
for (unsigned i = usedVectorLength - count; i < usedVectorLength; ++i)
731
storage->m_vector[i].clear();
737
bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
741
switch (structure()->indexingType()) {
745
case ArrayWithUndecided:
746
// Don't handle this because it's confusing and it shouldn't come up.
750
case ArrayWithContiguous: {
751
unsigned oldLength = m_butterfly->publicLength();
752
ASSERT(count <= oldLength);
754
// We may have to walk the entire array to do the shift. We're willing to do
755
// so only if it's not horribly slow.
756
if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
757
return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData()));
759
unsigned end = oldLength - count;
760
for (unsigned i = startIndex; i < end; ++i) {
761
// Storing to a hole is fine since we're still having a good time. But reading
762
// from a hole is totally not fine, since we might have to read from the proto
764
JSValue v = m_butterfly->contiguous()[i + count].get();
766
// The purpose of this path is to ensure that we don't make the same
767
// mistake in the future: shiftCountWithArrayStorage() can't do anything
768
// about holes (at least for now), but it can detect them quickly. So
769
// we convert to array storage and then allow the array storage path to
771
return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData()));
773
// No need for a barrier since we're just moving data around in the same vector.
774
// This is in line with our standing assumption that we won't have a deletion
776
m_butterfly->contiguous()[i].setWithoutWriteBarrier(v);
778
for (unsigned i = end; i < oldLength; ++i)
779
m_butterfly->contiguous()[i].clear();
781
m_butterfly->setPublicLength(oldLength - count);
785
case ArrayWithDouble: {
786
unsigned oldLength = m_butterfly->publicLength();
787
ASSERT(count <= oldLength);
789
// We may have to walk the entire array to do the shift. We're willing to do
790
// so only if it's not horribly slow.
791
if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
792
return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData()));
794
unsigned end = oldLength - count;
795
for (unsigned i = startIndex; i < end; ++i) {
796
// Storing to a hole is fine since we're still having a good time. But reading
797
// from a hole is totally not fine, since we might have to read from the proto
799
double v = m_butterfly->contiguousDouble()[i + count];
800
if (UNLIKELY(v != v)) {
801
// The purpose of this path is to ensure that we don't make the same
802
// mistake in the future: shiftCountWithArrayStorage() can't do anything
803
// about holes (at least for now), but it can detect them quickly. So
804
// we convert to array storage and then allow the array storage path to
806
return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->globalData()));
808
// No need for a barrier since we're just moving data around in the same vector.
809
// This is in line with our standing assumption that we won't have a deletion
811
m_butterfly->contiguousDouble()[i] = v;
813
for (unsigned i = end; i < oldLength; ++i)
814
m_butterfly->contiguousDouble()[i] = QNaN;
816
m_butterfly->setPublicLength(oldLength - count);
820
case ArrayWithArrayStorage:
821
case ArrayWithSlowPutArrayStorage:
822
return shiftCountWithArrayStorage(startIndex, count, arrayStorage());
830
// Returns true if the unshift can be handled, false to fallback.
831
bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
833
unsigned length = storage->length();
835
ASSERT(startIndex <= length);
837
// If the array contains holes or is otherwise in an abnormal state,
838
// use the generic algorithm in ArrayPrototype.
839
if (length != storage->m_numValuesInVector || storage->inSparseMode() || shouldUseSlowPut(structure()->indexingType()))
842
bool moveFront = !startIndex || startIndex < length / 2;
844
unsigned vectorLength = storage->vectorLength();
846
if (moveFront && storage->m_indexBias >= count) {
847
m_butterfly = storage->butterfly()->unshift(structure(), count);
848
storage = m_butterfly->arrayStorage();
849
storage->m_indexBias -= count;
850
storage->setVectorLength(vectorLength + count);
851
} else if (!moveFront && vectorLength - length >= count)
852
storage = storage->butterfly()->arrayStorage();
853
else if (unshiftCountSlowCase(exec->globalData(), moveFront, count))
854
storage = arrayStorage();
856
throwOutOfMemoryError(exec);
860
WriteBarrier<Unknown>* vector = storage->m_vector;
864
memmove(vector, vector + count, startIndex * sizeof(JSValue));
865
else if (length - startIndex)
866
memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
869
for (unsigned i = 0; i < count; i++)
870
vector[i + startIndex].clear();
874
bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
876
switch (structure()->indexingType()) {
878
case ArrayWithUndecided:
879
// We could handle this. But it shouldn't ever come up, so we won't.
883
case ArrayWithContiguous: {
884
unsigned oldLength = m_butterfly->publicLength();
886
// We may have to walk the entire array to do the unshift. We're willing to do so
887
// only if it's not horribly slow.
888
if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
889
return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData()));
891
ensureLength(exec->globalData(), oldLength + count);
893
for (unsigned i = oldLength; i-- > startIndex;) {
894
JSValue v = m_butterfly->contiguous()[i].get();
896
return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData()));
897
m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
900
// NOTE: we're leaving being garbage in the part of the array that we shifted out
901
// of. This is fine because the caller is required to store over that area, and
902
// in contiguous mode storing into a hole is guaranteed to behave exactly the same
903
// as storing over an existing element.
908
case ArrayWithDouble: {
909
unsigned oldLength = m_butterfly->publicLength();
911
// We may have to walk the entire array to do the unshift. We're willing to do so
912
// only if it's not horribly slow.
913
if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
914
return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData()));
916
ensureLength(exec->globalData(), oldLength + count);
918
for (unsigned i = oldLength; i-- > startIndex;) {
919
double v = m_butterfly->contiguousDouble()[i];
920
if (UNLIKELY(v != v))
921
return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->globalData()));
922
m_butterfly->contiguousDouble()[i + count] = v;
925
// NOTE: we're leaving being garbage in the part of the array that we shifted out
926
// of. This is fine because the caller is required to store over that area, and
927
// in contiguous mode storing into a hole is guaranteed to behave exactly the same
928
// as storing over an existing element.
933
case ArrayWithArrayStorage:
934
case ArrayWithSlowPutArrayStorage:
935
return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
943
static int compareNumbersForQSortWithInt32(const void* a, const void* b)
945
int32_t ia = static_cast<const JSValue*>(a)->asInt32();
946
int32_t ib = static_cast<const JSValue*>(b)->asInt32();
950
static int compareNumbersForQSortWithDouble(const void* a, const void* b)
952
double da = *static_cast<const double*>(a);
953
double db = *static_cast<const double*>(b);
954
return (da > db) - (da < db);
957
static int compareNumbersForQSort(const void* a, const void* b)
959
double da = static_cast<const JSValue*>(a)->asNumber();
960
double db = static_cast<const JSValue*>(b)->asNumber();
961
return (da > db) - (da < db);
964
static int compareByStringPairForQSort(const void* a, const void* b)
966
const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
967
const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
968
return codePointCompare(va->second, vb->second);
971
template<IndexingType indexingType>
972
void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
974
ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage);
976
unsigned lengthNotIncludingUndefined;
977
unsigned newRelevantLength;
978
compactForSorting<indexingType>(
979
lengthNotIncludingUndefined,
982
WriteBarrier<Unknown>* data = indexingData<indexingType>();
984
if (indexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
985
throwOutOfMemoryError(exec);
989
if (!lengthNotIncludingUndefined)
992
bool allValuesAreNumbers = true;
993
switch (indexingType) {
995
case ArrayWithDouble:
999
for (size_t i = 0; i < newRelevantLength; ++i) {
1000
if (!data[i].isNumber()) {
1001
allValuesAreNumbers = false;
1008
if (!allValuesAreNumbers)
1009
return sort(exec, compareFunction, callType, callData);
1011
// For numeric comparison, which is fast, qsort is faster than mergesort. We
1012
// also don't require mergesort's stability, since there's no user visible
1013
// side-effect from swapping the order of equal primitive values.
1014
int (*compare)(const void*, const void*);
1015
switch (indexingType) {
1016
case ArrayWithInt32:
1017
compare = compareNumbersForQSortWithInt32;
1020
case ArrayWithDouble:
1021
compare = compareNumbersForQSortWithDouble;
1022
ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(double));
1026
compare = compareNumbersForQSort;
1030
qsort(data, newRelevantLength, sizeof(WriteBarrier<Unknown>), compare);
1034
void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1036
ASSERT(!inSparseIndexingMode());
1038
switch (structure()->indexingType()) {
1042
case ArrayWithInt32:
1043
sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
1046
case ArrayWithDouble:
1047
sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
1050
case ArrayWithContiguous:
1051
sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
1054
case ArrayWithArrayStorage:
1055
sortNumericVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
1064
template<IndexingType indexingType>
1065
void JSArray::sortCompactedVector(ExecState* exec, void* begin, unsigned relevantLength)
1067
if (!relevantLength)
1070
JSGlobalData& globalData = exec->globalData();
1072
// Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
1073
// This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
1074
// buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
1075
// random or otherwise changing results, effectively making compare function inconsistent.
1077
Vector<ValueStringPair> values(relevantLength);
1078
if (!values.begin()) {
1079
throwOutOfMemoryError(exec);
1083
Heap::heap(this)->pushTempSortVector(&values);
1085
bool isSortingPrimitiveValues = true;
1086
switch (indexingType) {
1087
case ArrayWithInt32:
1088
for (size_t i = 0; i < relevantLength; i++) {
1089
JSValue value = static_cast<WriteBarrier<Unknown>*>(begin)[i].get();
1090
ASSERT(value.isInt32());
1091
values[i].first = value;
1095
case ArrayWithDouble:
1096
for (size_t i = 0; i < relevantLength; i++) {
1097
double value = static_cast<double*>(begin)[i];
1098
ASSERT(value == value);
1099
values[i].first = JSValue(JSValue::EncodeAsDouble, value);
1104
for (size_t i = 0; i < relevantLength; i++) {
1105
JSValue value = static_cast<WriteBarrier<Unknown>*>(begin)[i].get();
1106
ASSERT(!value.isUndefined());
1107
values[i].first = value;
1108
isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
1113
// FIXME: The following loop continues to call toString on subsequent values even after
1114
// a toString call raises an exception.
1116
for (size_t i = 0; i < relevantLength; i++)
1117
values[i].second = values[i].first.toWTFStringInline(exec);
1119
if (exec->hadException()) {
1120
Heap::heap(this)->popTempSortVector(&values);
1124
// FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
1128
if (isSortingPrimitiveValues)
1129
qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1131
mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1133
// FIXME: The qsort library function is likely to not be a stable sort.
1134
// ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
1135
qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
1138
// If the toString function changed the length of the array or vector storage,
1139
// increase the length to handle the orignal number of actual values.
1140
switch (indexingType) {
1141
case ArrayWithInt32:
1142
case ArrayWithDouble:
1143
case ArrayWithContiguous:
1144
ensureLength(globalData, relevantLength);
1147
case ArrayWithArrayStorage:
1148
if (arrayStorage()->vectorLength() < relevantLength) {
1149
increaseVectorLength(exec->globalData(), relevantLength);
1150
begin = arrayStorage()->m_vector;
1152
if (arrayStorage()->length() < relevantLength)
1153
arrayStorage()->setLength(relevantLength);
1160
for (size_t i = 0; i < relevantLength; i++) {
1161
if (indexingType == ArrayWithDouble)
1162
static_cast<double*>(begin)[i] = values[i].first.asNumber();
1164
static_cast<WriteBarrier<Unknown>*>(begin)[i].set(globalData, this, values[i].first);
1167
Heap::heap(this)->popTempSortVector(&values);
1170
void JSArray::sort(ExecState* exec)
1172
ASSERT(!inSparseIndexingMode());
1174
switch (structure()->indexingType()) {
1176
case ArrayWithUndecided:
1179
case ArrayWithInt32: {
1180
unsigned lengthNotIncludingUndefined;
1181
unsigned newRelevantLength;
1182
compactForSorting<ArrayWithInt32>(
1183
lengthNotIncludingUndefined, newRelevantLength);
1185
sortCompactedVector<ArrayWithInt32>(
1186
exec, m_butterfly->contiguousInt32(), lengthNotIncludingUndefined);
1190
case ArrayWithDouble: {
1191
unsigned lengthNotIncludingUndefined;
1192
unsigned newRelevantLength;
1193
compactForSorting<ArrayWithDouble>(
1194
lengthNotIncludingUndefined, newRelevantLength);
1196
sortCompactedVector<ArrayWithDouble>(
1197
exec, m_butterfly->contiguousDouble(), lengthNotIncludingUndefined);
1201
case ArrayWithContiguous: {
1202
unsigned lengthNotIncludingUndefined;
1203
unsigned newRelevantLength;
1204
compactForSorting<ArrayWithContiguous>(
1205
lengthNotIncludingUndefined, newRelevantLength);
1207
sortCompactedVector<ArrayWithContiguous>(
1208
exec, m_butterfly->contiguous(), lengthNotIncludingUndefined);
1212
case ArrayWithArrayStorage: {
1213
unsigned lengthNotIncludingUndefined;
1214
unsigned newRelevantLength;
1215
compactForSorting<ArrayWithArrayStorage>(
1216
lengthNotIncludingUndefined, newRelevantLength);
1217
ArrayStorage* storage = m_butterfly->arrayStorage();
1218
ASSERT(!storage->m_sparseMap);
1220
sortCompactedVector<ArrayWithArrayStorage>(
1221
exec, storage->m_vector, lengthNotIncludingUndefined);
1226
ASSERT_NOT_REACHED();
1230
struct AVLTreeNodeForArrayCompare {
1233
// Child pointers. The high bit of gt is robbed and used as the
1234
// balance factor sign. The high bit of lt is robbed and used as
1235
// the magnitude of the balance factor.
1240
struct AVLTreeAbstractorForArrayCompare {
1241
typedef int32_t handle; // Handle is an index into m_nodes vector.
1242
typedef JSValue key;
1243
typedef int32_t size;
1245
Vector<AVLTreeNodeForArrayCompare> m_nodes;
1247
JSValue m_compareFunction;
1248
CallType m_compareCallType;
1249
const CallData* m_compareCallData;
1250
OwnPtr<CachedCall> m_cachedCall;
1252
handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
1253
void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
1254
handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
1255
void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
1257
int get_balance_factor(handle h)
1259
if (m_nodes[h].gt & 0x80000000)
1261
return static_cast<unsigned>(m_nodes[h].lt) >> 31;
1264
void set_balance_factor(handle h, int bf)
1267
m_nodes[h].lt &= 0x7FFFFFFF;
1268
m_nodes[h].gt &= 0x7FFFFFFF;
1270
m_nodes[h].lt |= 0x80000000;
1272
m_nodes[h].gt |= 0x80000000;
1274
m_nodes[h].gt &= 0x7FFFFFFF;
1278
int compare_key_key(key va, key vb)
1280
ASSERT(!va.isUndefined());
1281
ASSERT(!vb.isUndefined());
1283
if (m_exec->hadException())
1286
double compareResult;
1288
m_cachedCall->setThis(jsUndefined());
1289
m_cachedCall->setArgument(0, va);
1290
m_cachedCall->setArgument(1, vb);
1291
compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec));
1293
MarkedArgumentBuffer arguments;
1294
arguments.append(va);
1295
arguments.append(vb);
1296
compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, jsUndefined(), arguments).toNumber(m_exec);
1298
return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
1301
int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
1302
int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
1304
static handle null() { return 0x7FFFFFFF; }
1307
template<IndexingType indexingType>
1308
void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1310
ASSERT(!inSparseIndexingMode());
1311
ASSERT(indexingType == structure()->indexingType());
1313
// FIXME: This ignores exceptions raised in the compare function or in toNumber.
1315
// The maximum tree depth is compiled in - but the caller is clearly up to no good
1316
// if a larger array is passed.
1317
ASSERT(m_butterfly->publicLength() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
1318
if (m_butterfly->publicLength() > static_cast<unsigned>(std::numeric_limits<int>::max()))
1321
unsigned usedVectorLength = relevantLength<indexingType>();
1322
unsigned nodeCount = usedVectorLength;
1327
AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
1328
tree.abstractor().m_exec = exec;
1329
tree.abstractor().m_compareFunction = compareFunction;
1330
tree.abstractor().m_compareCallType = callType;
1331
tree.abstractor().m_compareCallData = &callData;
1332
tree.abstractor().m_nodes.grow(nodeCount);
1334
if (callType == CallTypeJS)
1335
tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
1337
if (!tree.abstractor().m_nodes.begin()) {
1338
throwOutOfMemoryError(exec);
1342
// FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
1343
// right out from under us while we're building the tree here.
1345
unsigned numDefined = 0;
1346
unsigned numUndefined = 0;
1348
// Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
1349
for (; numDefined < usedVectorLength; ++numDefined) {
1350
if (numDefined > m_butterfly->vectorLength())
1352
JSValue v = getHolyIndexQuickly(numDefined);
1353
if (!v || v.isUndefined())
1355
tree.abstractor().m_nodes[numDefined].value = v;
1356
tree.insert(numDefined);
1358
for (unsigned i = numDefined; i < usedVectorLength; ++i) {
1359
if (i > m_butterfly->vectorLength())
1361
JSValue v = getHolyIndexQuickly(i);
1363
if (v.isUndefined())
1366
tree.abstractor().m_nodes[numDefined].value = v;
1367
tree.insert(numDefined);
1373
unsigned newUsedVectorLength = numDefined + numUndefined;
1375
// The array size may have changed. Figure out the new bounds.
1376
unsigned newestUsedVectorLength = currentRelevantLength();
1378
unsigned elementsToExtractThreshold = min(min(newestUsedVectorLength, numDefined), static_cast<unsigned>(tree.abstractor().m_nodes.size()));
1379
unsigned undefinedElementsThreshold = min(newestUsedVectorLength, newUsedVectorLength);
1380
unsigned clearElementsThreshold = min(newestUsedVectorLength, usedVectorLength);
1382
// Copy the values back into m_storage.
1383
AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
1384
iter.start_iter_least(tree);
1385
JSGlobalData& globalData = exec->globalData();
1386
for (unsigned i = 0; i < elementsToExtractThreshold; ++i) {
1387
if (structure()->indexingType() == ArrayWithDouble)
1388
butterfly()->contiguousDouble()[i] = tree.abstractor().m_nodes[*iter].value.asNumber();
1390
currentIndexingData()[i].set(globalData, this, tree.abstractor().m_nodes[*iter].value);
1393
// Put undefined values back in.
1394
switch (structure()->indexingType()) {
1395
case ArrayWithInt32:
1396
case ArrayWithDouble:
1397
ASSERT(elementsToExtractThreshold == undefinedElementsThreshold);
1401
for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i)
1402
currentIndexingData()[i].setUndefined();
1405
// Ensure that unused values in the vector are zeroed out.
1406
for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) {
1407
if (structure()->indexingType() == ArrayWithDouble)
1408
butterfly()->contiguousDouble()[i] = QNaN;
1410
currentIndexingData()[i].clear();
1413
if (hasArrayStorage(structure()->indexingType()))
1414
arrayStorage()->m_numValuesInVector = newUsedVectorLength;
1417
void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
1419
ASSERT(!inSparseIndexingMode());
1421
switch (structure()->indexingType()) {
1423
case ArrayWithUndecided:
1426
case ArrayWithInt32:
1427
sortVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
1430
case ArrayWithDouble:
1431
sortVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
1434
case ArrayWithContiguous:
1435
sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
1438
case ArrayWithArrayStorage:
1439
sortVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
1443
ASSERT_NOT_REACHED();
1447
void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1451
WriteBarrier<Unknown>* vector;
1453
switch (structure()->indexingType()) {
1457
case ArrayWithUndecided: {
1463
case ArrayWithInt32:
1464
case ArrayWithContiguous: {
1465
vectorEnd = m_butterfly->publicLength();
1466
vector = m_butterfly->contiguous();
1470
case ArrayWithDouble: {
1473
for (; i < m_butterfly->publicLength(); ++i) {
1474
double v = butterfly()->contiguousDouble()[i];
1477
args.append(JSValue(JSValue::EncodeAsDouble, v));
1482
case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1483
ArrayStorage* storage = m_butterfly->arrayStorage();
1485
vector = storage->m_vector;
1486
vectorEnd = min(storage->length(), storage->vectorLength());
1497
for (; i < vectorEnd; ++i) {
1498
WriteBarrier<Unknown>& v = vector[i];
1501
args.append(v.get());
1504
for (; i < length(); ++i)
1505
args.append(get(exec, i));
1508
void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
1511
WriteBarrier<Unknown>* vector;
1514
ASSERT(length == this->length());
1515
switch (structure()->indexingType()) {
1519
case ArrayWithUndecided: {
1525
case ArrayWithInt32:
1526
case ArrayWithContiguous: {
1527
vector = m_butterfly->contiguous();
1528
vectorEnd = m_butterfly->publicLength();
1532
case ArrayWithDouble: {
1535
for (; i < m_butterfly->publicLength(); ++i) {
1536
double v = m_butterfly->contiguousDouble()[i];
1539
callFrame->setArgument(i, JSValue(JSValue::EncodeAsDouble, v));
1544
case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1545
ArrayStorage* storage = m_butterfly->arrayStorage();
1546
vector = storage->m_vector;
1547
vectorEnd = min(length, storage->vectorLength());
1558
for (; i < vectorEnd; ++i) {
1559
WriteBarrier<Unknown>& v = vector[i];
1562
callFrame->setArgument(i, v.get());
1565
for (; i < length; ++i)
1566
callFrame->setArgument(i, get(exec, i));
1569
template<IndexingType indexingType>
1570
void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLength)
1572
ASSERT(!inSparseIndexingMode());
1573
ASSERT(indexingType == structure()->indexingType());
1575
unsigned myRelevantLength = relevantLength<indexingType>();
1578
unsigned numUndefined = 0;
1580
for (; numDefined < myRelevantLength; ++numDefined) {
1581
if (indexingType == ArrayWithInt32) {
1582
JSValue v = m_butterfly->contiguousInt32()[numDefined].get();
1585
ASSERT(v.isInt32());
1588
if (indexingType == ArrayWithDouble) {
1589
double v = m_butterfly->contiguousDouble()[numDefined];
1594
JSValue v = indexingData<indexingType>()[numDefined].get();
1595
if (!v || v.isUndefined())
1599
for (unsigned i = numDefined; i < myRelevantLength; ++i) {
1600
if (indexingType == ArrayWithInt32) {
1601
JSValue v = m_butterfly->contiguousInt32()[i].get();
1604
ASSERT(v.isInt32());
1605
m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v);
1608
if (indexingType == ArrayWithDouble) {
1609
double v = m_butterfly->contiguousDouble()[i];
1612
m_butterfly->contiguousDouble()[numDefined++] = v;
1615
JSValue v = indexingData<indexingType>()[i].get();
1617
if (v.isUndefined())
1620
indexingData<indexingType>()[numDefined++].setWithoutWriteBarrier(v);
1624
newRelevantLength = numDefined + numUndefined;
1626
if (hasArrayStorage(indexingType))
1627
ASSERT(!arrayStorage()->m_sparseMap);
1629
switch (indexingType) {
1630
case ArrayWithInt32:
1631
case ArrayWithDouble:
1632
ASSERT(numDefined == newRelevantLength);
1636
for (unsigned i = numDefined; i < newRelevantLength; ++i)
1637
indexingData<indexingType>()[i].setUndefined();
1640
for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) {
1641
if (indexingType == ArrayWithDouble)
1642
m_butterfly->contiguousDouble()[i] = QNaN;
1644
indexingData<indexingType>()[i].clear();
1647
if (hasArrayStorage(indexingType))
1648
arrayStorage()->m_numValuesInVector = newRelevantLength;