[JSC] Array.prototype.reverse modifies JSImmutableButterfly
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSArray.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003-2018 Apple Inc. All rights reserved.
4  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
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 "ButterflyInlines.h"
28 #include "CodeBlock.h"
29 #include "Error.h"
30 #include "GetterSetter.h"
31 #include "IndexingHeaderInlines.h"
32 #include "JSArrayInlines.h"
33 #include "JSCInlines.h"
34 #include "PropertyNameArray.h"
35 #include "TypeError.h"
36 #include <wtf/Assertions.h>
37
38 namespace JSC {
39
40 const ASCIILiteral LengthExceededTheMaximumArrayLengthError { "Length exceeded the maximum array length"_s };
41
42 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
43
44 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArray)};
45
46 JSArray* JSArray::tryCreateUninitializedRestricted(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
47 {
48     VM& vm = scope.vm();
49
50     if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH))
51         return nullptr;
52
53     unsigned outOfLineStorage = structure->outOfLineCapacity();
54     Butterfly* butterfly;
55     IndexingType indexingType = structure->indexingType();
56     if (LIKELY(!hasAnyArrayStorage(indexingType))) {
57         ASSERT(
58             hasUndecided(indexingType)
59             || hasInt32(indexingType)
60             || hasDouble(indexingType)
61             || hasContiguous(indexingType));
62
63         unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength);
64         void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(
65             vm,
66             Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)),
67             deferralContext, AllocationFailureMode::ReturnNull);
68         if (UNLIKELY(!temp))
69             return nullptr;
70         butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
71         butterfly->setVectorLength(vectorLength);
72         butterfly->setPublicLength(initialLength);
73         if (hasDouble(indexingType)) {
74             for (unsigned i = initialLength; i < vectorLength; ++i)
75                 butterfly->contiguousDouble().atUnsafe(i) = PNaN;
76         } else {
77             for (unsigned i = initialLength; i < vectorLength; ++i)
78                 butterfly->contiguous().atUnsafe(i).clear();
79         }
80     } else {
81         ASSERT(
82             indexingType == ArrayWithSlowPutArrayStorage
83             || indexingType == ArrayWithArrayStorage);
84         static const unsigned indexBias = 0;
85         unsigned vectorLength = ArrayStorage::optimalVectorLength(indexBias, structure, initialLength);
86         void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(
87             vm,
88             Butterfly::totalSize(indexBias, outOfLineStorage, true, ArrayStorage::sizeFor(vectorLength)),
89             deferralContext, AllocationFailureMode::ReturnNull);
90         if (UNLIKELY(!temp))
91             return nullptr;
92         butterfly = Butterfly::fromBase(temp, indexBias, outOfLineStorage);
93         *butterfly->indexingHeader() = indexingHeaderForArrayStorage(initialLength, vectorLength);
94         ArrayStorage* storage = butterfly->arrayStorage();
95         storage->m_indexBias = indexBias;
96         storage->m_sparseMap.clear();
97         storage->m_numValuesInVector = initialLength;
98         for (unsigned i = initialLength; i < vectorLength; ++i)
99             storage->m_vector[i].clear();
100     }
101
102     JSArray* result = createWithButterfly(vm, deferralContext, structure, butterfly);
103
104     const bool createUninitialized = true;
105     scope.notifyAllocated(result, createUninitialized);
106     return result;
107 }
108
109 void JSArray::eagerlyInitializeButterfly(ObjectInitializationScope& scope, JSArray* array, unsigned initialLength)
110 {
111     Structure* structure = array->structure(scope.vm());
112     IndexingType indexingType = structure->indexingType();
113     Butterfly* butterfly = array->butterfly();
114
115     // This function only serves as a companion to tryCreateUninitializedRestricted()
116     // in the event that we really can't defer initialization of the butterfly after all.
117     // tryCreateUninitializedRestricted() already initialized the elements between
118     // initialLength and vector length. We just need to do 0 - initialLength.
119     // ObjectInitializationScope::notifyInitialized() will verify that all elements are
120     // initialized.
121     if (LIKELY(!hasAnyArrayStorage(indexingType))) {
122         if (hasDouble(indexingType)) {
123             for (unsigned i = 0; i < initialLength; ++i)
124                 butterfly->contiguousDouble().atUnsafe(i) = PNaN;
125         } else {
126             for (unsigned i = 0; i < initialLength; ++i)
127                 butterfly->contiguous().atUnsafe(i).clear();
128         }
129     } else {
130         ArrayStorage* storage = butterfly->arrayStorage();
131         for (unsigned i = 0; i < initialLength; ++i)
132             storage->m_vector[i].clear();
133     }
134     scope.notifyInitialized(array);
135 }
136
137 void JSArray::setLengthWritable(ExecState* exec, bool writable)
138 {
139     ASSERT(isLengthWritable() || !writable);
140     if (!isLengthWritable() || writable)
141         return;
142
143     enterDictionaryIndexingMode(exec->vm());
144
145     SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
146     ASSERT(map);
147     map->setLengthIsReadOnly();
148 }
149
150 // Defined in ES5.1 15.4.5.1
151 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
152 {
153     VM& vm = exec->vm();
154     auto scope = DECLARE_THROW_SCOPE(vm);
155
156     JSArray* array = jsCast<JSArray*>(object);
157
158     // 3. If P is "length", then
159     if (propertyName == vm.propertyNames->length) {
160         // All paths through length definition call the default [[DefineOwnProperty]], hence:
161         // from ES5.1 8.12.9 7.a.
162         if (descriptor.configurablePresent() && descriptor.configurable())
163             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
164         // from ES5.1 8.12.9 7.b.
165         if (descriptor.enumerablePresent() && descriptor.enumerable())
166             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
167
168         // a. If the [[Value]] field of Desc is absent, then
169         // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
170         if (descriptor.isAccessorDescriptor())
171             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
172         // from ES5.1 8.12.9 10.a.
173         if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
174             return typeError(exec, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
175         // This descriptor is either just making length read-only, or changing nothing!
176         if (!descriptor.value()) {
177             if (descriptor.writablePresent())
178                 array->setLengthWritable(exec, descriptor.writable());
179             return true;
180         }
181         
182         // b. Let newLenDesc be a copy of Desc.
183         // c. Let newLen be ToUint32(Desc.[[Value]]).
184         unsigned newLen = descriptor.value().toUInt32(exec);
185         RETURN_IF_EXCEPTION(scope, false);
186         // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
187         double valueAsNumber = descriptor.value().toNumber(exec);
188         RETURN_IF_EXCEPTION(scope, false);
189         if (newLen != valueAsNumber) {
190             JSC::throwException(exec, scope, createRangeError(exec, "Invalid array length"_s));
191             return false;
192         }
193
194         // Based on SameValue check in 8.12.9, this is always okay.
195         // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
196         if (newLen == array->length()) {
197             if (descriptor.writablePresent())
198                 array->setLengthWritable(exec, descriptor.writable());
199             return true;
200         }
201
202         // e. Set newLenDesc.[[Value] to newLen.
203         // f. If newLen >= oldLen, then
204         // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
205         // g. Reject if oldLenDesc.[[Writable]] is false.
206         if (!array->isLengthWritable())
207             return typeError(exec, scope, throwException, ReadonlyPropertyChangeError);
208         
209         // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
210         // i. Else,
211         // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
212         // i.ii. Let newWritable be false.
213         // i.iii. Set newLenDesc.[[Writable] to true.
214         // 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.
215         // k. If succeeded is false, return false.
216         // l. While newLen < oldLen repeat,
217         // l.i. Set oldLen to oldLen – 1.
218         // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
219         // l.iii. If deleteSucceeded is false, then
220         bool success = array->setLength(exec, newLen, throwException);
221         EXCEPTION_ASSERT(!scope.exception() || !success);
222         if (!success) {
223             // 1. Set newLenDesc.[[Value] to oldLen+1.
224             // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
225             // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
226             // 4. Reject.
227             if (descriptor.writablePresent())
228                 array->setLengthWritable(exec, descriptor.writable());
229             return false;
230         }
231
232         // m. If newWritable is false, then
233         // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
234         //    Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
235         //    return true.
236         if (descriptor.writablePresent())
237             array->setLengthWritable(exec, descriptor.writable());
238         // n. Return true.
239         return true;
240     }
241
242     // 4. Else if P is an array index (15.4), then
243     // a. Let index be ToUint32(P).
244     if (std::optional<uint32_t> optionalIndex = parseIndex(propertyName)) {
245         // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
246         uint32_t index = optionalIndex.value();
247         // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
248         if (index >= array->length() && !array->isLengthWritable())
249             return typeError(exec, scope, throwException, "Attempting to define numeric property on array with non-writable length property."_s);
250         // 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.
251         // d. Reject if succeeded is false.
252         // e. If index >= oldLen
253         // e.i. Set oldLenDesc.[[Value]] to index + 1.
254         // 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.
255         // f. Return true.
256         scope.release();
257         return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
258     }
259
260     scope.release();
261     return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
262 }
263
264 bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
265 {
266     VM& vm = exec->vm();
267     JSArray* thisObject = jsCast<JSArray*>(object);
268     if (propertyName == vm.propertyNames->length) {
269         unsigned attributes = thisObject->isLengthWritable() ? PropertyAttribute::DontDelete | PropertyAttribute::DontEnum : PropertyAttribute::DontDelete | PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
270         slot.setValue(thisObject, attributes, jsNumber(thisObject->length()));
271         return true;
272     }
273
274     return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
275 }
276
277 // ECMA 15.4.5.1
278 bool JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
279 {
280     VM& vm = exec->vm();
281     auto scope = DECLARE_THROW_SCOPE(vm);
282
283     JSArray* thisObject = jsCast<JSArray*>(cell);
284
285     if (UNLIKELY(isThisValueAltered(slot, thisObject))) {
286         scope.release();
287         return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
288     }
289
290     thisObject->ensureWritable(vm);
291
292     if (propertyName == vm.propertyNames->length) {
293         if (!thisObject->isLengthWritable())
294             return false;
295         unsigned newLength = value.toUInt32(exec);
296         RETURN_IF_EXCEPTION(scope, false);
297         double valueAsNumber = value.toNumber(exec);
298         RETURN_IF_EXCEPTION(scope, false);
299         if (valueAsNumber != static_cast<double>(newLength)) {
300             throwException(exec, scope, createRangeError(exec, "Invalid array length"_s));
301             return false;
302         }
303         scope.release();
304         return thisObject->setLength(exec, newLength, slot.isStrictMode());
305     }
306
307     scope.release();
308     return JSObject::put(thisObject, exec, propertyName, value, slot);
309 }
310
311 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
312 {
313     VM& vm = exec->vm();
314     JSArray* thisObject = jsCast<JSArray*>(cell);
315
316     if (propertyName == vm.propertyNames->length)
317         return false;
318
319     return JSObject::deleteProperty(thisObject, exec, propertyName);
320 }
321
322 static int compareKeysForQSort(const void* a, const void* b)
323 {
324     unsigned da = *static_cast<const unsigned*>(a);
325     unsigned db = *static_cast<const unsigned*>(b);
326     return (da > db) - (da < db);
327 }
328
329 void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
330 {
331     VM& vm = exec->vm();
332     JSArray* thisObject = jsCast<JSArray*>(object);
333
334     if (mode.includeDontEnumProperties())
335         propertyNames.add(vm.propertyNames->length);
336
337     JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
338 }
339
340 // This method makes room in the vector, but leaves the new space for count slots uncleared.
341 bool JSArray::unshiftCountSlowCase(const AbstractLocker&, VM& vm, DeferGC&, bool addToFront, unsigned count)
342 {
343     ArrayStorage* storage = ensureArrayStorage(vm);
344     Butterfly* butterfly = storage->butterfly();
345     Structure* structure = this->structure(vm);
346     unsigned propertyCapacity = structure->outOfLineCapacity();
347     unsigned propertySize = structure->outOfLineSize();
348     
349     // If not, we should have handled this on the fast path.
350     ASSERT(!addToFront || count > storage->m_indexBias);
351
352     // Step 1:
353     // Gather 4 key metrics:
354     //  * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
355     //  * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
356     //  * currentCapacity - what is the current size of the vector, including any pre-capacity.
357     //  * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
358
359     unsigned length = storage->length();
360     unsigned oldVectorLength = storage->vectorLength();
361     unsigned usedVectorLength = std::min(oldVectorLength, length);
362     ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
363     // Check that required vector length is possible, in an overflow-safe fashion.
364     if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
365         return false;
366     unsigned requiredVectorLength = usedVectorLength + count;
367     ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
368     // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
369     ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
370     unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
371     // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
372     // FIXME: This code should be fixed to avoid internal fragmentation. It's not super high
373     // priority since increaseVectorLength() will "fix" any mistakes we make, but it would be cool
374     // to get this right eventually.
375     unsigned desiredCapacity = std::min(MAX_STORAGE_VECTOR_LENGTH, std::max(BASE_ARRAY_STORAGE_VECTOR_LEN, requiredVectorLength) << 1);
376
377     // Step 2:
378     // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
379
380     void* newAllocBase = nullptr;
381     unsigned newStorageCapacity;
382     bool allocatedNewStorage;
383     // If the current storage array is sufficiently large (but not too large!) then just keep using it.
384     if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
385         newAllocBase = butterfly->base(structure);
386         newStorageCapacity = currentCapacity;
387         allocatedNewStorage = false;
388     } else {
389         const unsigned preCapacity = 0;
390         Butterfly* newButterfly = Butterfly::tryCreateUninitialized(vm, this, preCapacity, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
391         if (!newButterfly)
392             return false;
393         newAllocBase = newButterfly->base(preCapacity, propertyCapacity);
394         newStorageCapacity = desiredCapacity;
395         allocatedNewStorage = true;
396     }
397
398     // Step 3:
399     // Work out where we're going to move things to.
400
401     // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
402     // If we're adding to the end, we'll add all the new space to the end.
403     // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
404     // If it did, we calculate the amount that will remain based on an atomic decay - leave the
405     // vector with half the post-capacity it had previously.
406     unsigned postCapacity = 0;
407     if (!addToFront)
408         postCapacity = newStorageCapacity - requiredVectorLength;
409     else if (length < storage->vectorLength()) {
410         // Atomic decay, + the post-capacity cannot be greater than what is available.
411         postCapacity = std::min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
412         // If we're moving contents within the same allocation, the post-capacity is being reduced.
413         ASSERT(newAllocBase != butterfly->base(structure) || postCapacity < storage->vectorLength() - length);
414     }
415
416     unsigned newVectorLength = requiredVectorLength + postCapacity;
417     RELEASE_ASSERT(newVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
418     unsigned preCapacity = newStorageCapacity - newVectorLength;
419
420     Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, preCapacity, propertyCapacity);
421
422     if (addToFront) {
423         ASSERT(count + usedVectorLength <= newVectorLength);
424         memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
425         memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
426
427         if (allocatedNewStorage) {
428             // We will set the vectorLength to newVectorLength. We populated requiredVectorLength
429             // (usedVectorLength + count), which is less. Clear the difference.
430             for (unsigned i = requiredVectorLength; i < newVectorLength; ++i)
431                 newButterfly->arrayStorage()->m_vector[i].clear();
432             // We don't need to zero the pre-capacity because it is not available to use as property storage.
433             memset(newButterfly->base(0, propertyCapacity), 0, (propertyCapacity - propertySize) * sizeof(JSValue));
434         }
435     } else if ((newAllocBase != butterfly->base(structure)) || (preCapacity != storage->m_indexBias)) {
436         memmove(newButterfly->propertyStorage() - propertyCapacity, butterfly->propertyStorage() - propertyCapacity, sizeof(JSValue) * propertyCapacity + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
437         memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
438         
439         for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
440             newButterfly->arrayStorage()->m_vector[i].clear();
441     }
442
443     newButterfly->arrayStorage()->setVectorLength(newVectorLength);
444     newButterfly->arrayStorage()->m_indexBias = preCapacity;
445     
446     setButterfly(vm, newButterfly);
447
448     return true;
449 }
450
451 bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
452 {
453     VM& vm = exec->vm();
454     auto scope = DECLARE_THROW_SCOPE(vm);
455
456     unsigned length = storage->length();
457     
458     // If the length is read only then we enter sparse mode, so should enter the following 'if'.
459     ASSERT(isLengthWritable() || storage->m_sparseMap);
460
461     if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
462         // Fail if the length is not writable.
463         if (map->lengthIsReadOnly())
464             return typeError(exec, scope, throwException, ReadonlyPropertyWriteError);
465
466         if (newLength < length) {
467             // Copy any keys we might be interested in into a vector.
468             Vector<unsigned, 0, UnsafeVectorOverflow> keys;
469             keys.reserveInitialCapacity(std::min(map->size(), static_cast<size_t>(length - newLength)));
470             SparseArrayValueMap::const_iterator end = map->end();
471             for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
472                 unsigned index = static_cast<unsigned>(it->key);
473                 if (index < length && index >= newLength)
474                     keys.append(index);
475             }
476
477             // Check if the array is in sparse mode. If so there may be non-configurable
478             // properties, so we have to perform deletion with caution, if not we can
479             // delete values in any order.
480             if (map->sparseMode()) {
481                 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
482                 unsigned i = keys.size();
483                 while (i) {
484                     unsigned index = keys[--i];
485                     SparseArrayValueMap::iterator it = map->find(index);
486                     ASSERT(it != map->notFound());
487                     if (it->value.attributes() & PropertyAttribute::DontDelete) {
488                         storage->setLength(index + 1);
489                         return typeError(exec, scope, throwException, UnableToDeletePropertyError);
490                     }
491                     map->remove(it);
492                 }
493             } else {
494                 for (unsigned i = 0; i < keys.size(); ++i)
495                     map->remove(keys[i]);
496                 if (map->isEmpty())
497                     deallocateSparseIndexMap();
498             }
499         }
500     }
501
502     if (newLength < length) {
503         // Delete properties from the vector.
504         unsigned usedVectorLength = std::min(length, storage->vectorLength());
505         for (unsigned i = newLength; i < usedVectorLength; ++i) {
506             WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
507             bool hadValue = !!valueSlot;
508             valueSlot.clear();
509             storage->m_numValuesInVector -= hadValue;
510         }
511     }
512
513     storage->setLength(newLength);
514
515     return true;
516 }
517
518 bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
519 {
520     auto scope = DECLARE_THROW_SCOPE(vm);
521
522     if (!canFastCopy(vm, otherArray))
523         return false;
524
525     IndexingType type = indexingType();
526     IndexingType otherType = otherArray->indexingType();
527     IndexingType copyType = mergeIndexingTypeForCopying(otherType);
528     if (type == ArrayWithUndecided && copyType != NonArray) {
529         if (copyType == ArrayWithInt32)
530             convertUndecidedToInt32(vm);
531         else if (copyType == ArrayWithDouble)
532             convertUndecidedToDouble(vm);
533         else if (copyType == ArrayWithContiguous)
534             convertUndecidedToContiguous(vm);
535         else {
536             ASSERT(copyType == ArrayWithUndecided);
537             return true;
538         }
539     } else if (type != copyType)
540         return false;
541
542     unsigned otherLength = otherArray->length();
543     Checked<unsigned, RecordOverflow> checkedNewLength = startIndex;
544     checkedNewLength += otherLength;
545
546     unsigned newLength;
547     if (checkedNewLength.safeGet(newLength) == CheckedState::DidOverflow) {
548         throwException(exec, scope, createRangeError(exec, LengthExceededTheMaximumArrayLengthError));
549         return false;
550     }
551
552     if (newLength >= MIN_SPARSE_ARRAY_INDEX)
553         return false;
554
555     if (!ensureLength(vm, newLength)) {
556         throwOutOfMemoryError(exec, scope);
557         return false;
558     }
559     ASSERT(copyType == indexingType());
560
561     if (UNLIKELY(otherType == ArrayWithUndecided)) {
562         auto* butterfly = this->butterfly();
563         if (type == ArrayWithDouble) {
564             for (unsigned i = startIndex; i < newLength; ++i)
565                 butterfly->contiguousDouble().at(this, i) = PNaN;
566         } else {
567             for (unsigned i = startIndex; i < newLength; ++i)
568                 butterfly->contiguousInt32().at(this, i).setWithoutWriteBarrier(JSValue());
569         }
570     } else if (type == ArrayWithDouble)
571         memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
572     else {
573         memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
574         vm.heap.writeBarrier(this);
575     }
576
577     return true;
578 }
579
580 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
581 {
582     VM& vm = exec->vm();
583     auto scope = DECLARE_THROW_SCOPE(vm);
584
585     Butterfly* butterfly = this->butterfly();
586     switch (indexingMode()) {
587     case ArrayClass:
588         if (!newLength)
589             return true;
590         if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
591             scope.release();
592             return setLengthWithArrayStorage(
593                 exec, newLength, throwException,
594                 ensureArrayStorage(vm));
595         }
596         createInitialUndecided(vm, newLength);
597         return true;
598
599     case CopyOnWriteArrayWithInt32:
600     case CopyOnWriteArrayWithDouble:
601     case CopyOnWriteArrayWithContiguous:
602         if (newLength == butterfly->publicLength())
603             return true;
604         convertFromCopyOnWrite(vm);
605         butterfly = this->butterfly();
606         FALLTHROUGH;
607
608     case ArrayWithUndecided:
609     case ArrayWithInt32:
610     case ArrayWithDouble:
611     case ArrayWithContiguous: {
612         if (newLength == butterfly->publicLength())
613             return true;
614         if (newLength > MAX_STORAGE_VECTOR_LENGTH // This check ensures that we can do fast push.
615             || (newLength >= MIN_SPARSE_ARRAY_INDEX
616                 && !isDenseEnoughForVector(newLength, countElements()))) {
617             scope.release();
618             return setLengthWithArrayStorage(
619                 exec, newLength, throwException,
620                 ensureArrayStorage(vm));
621         }
622         if (newLength > butterfly->publicLength()) {
623             if (!ensureLength(vm, newLength)) {
624                 throwOutOfMemoryError(exec, scope);
625                 return false;
626             }
627             return true;
628         }
629
630         unsigned lengthToClear = butterfly->publicLength() - newLength;
631         unsigned costToAllocateNewButterfly = 64; // a heuristic.
632         if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
633             reallocateAndShrinkButterfly(vm, newLength);
634             return true;
635         }
636
637         if (indexingType() == ArrayWithDouble) {
638             for (unsigned i = butterfly->publicLength(); i-- > newLength;)
639                 butterfly->contiguousDouble().at(this, i) = PNaN;
640         } else {
641             for (unsigned i = butterfly->publicLength(); i-- > newLength;)
642                 butterfly->contiguous().at(this, i).clear();
643         }
644         butterfly->setPublicLength(newLength);
645         return true;
646     }
647         
648     case ArrayWithArrayStorage:
649     case ArrayWithSlowPutArrayStorage:
650         scope.release();
651         return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
652         
653     default:
654         CRASH();
655         return false;
656     }
657 }
658
659 JSValue JSArray::pop(ExecState* exec)
660 {
661     VM& vm = exec->vm();
662     auto scope = DECLARE_THROW_SCOPE(vm);
663
664     ensureWritable(vm);
665
666     Butterfly* butterfly = this->butterfly();
667
668     switch (indexingType()) {
669     case ArrayClass:
670         return jsUndefined();
671         
672     case ArrayWithUndecided:
673         if (!butterfly->publicLength())
674             return jsUndefined();
675         // We have nothing but holes. So, drop down to the slow version.
676         break;
677         
678     case ArrayWithInt32:
679     case ArrayWithContiguous: {
680         unsigned length = butterfly->publicLength();
681         
682         if (!length--)
683             return jsUndefined();
684         
685         RELEASE_ASSERT(length < butterfly->vectorLength());
686         JSValue value = butterfly->contiguous().at(this, length).get();
687         if (value) {
688             butterfly->contiguous().at(this, length).clear();
689             butterfly->setPublicLength(length);
690             return value;
691         }
692         break;
693     }
694         
695     case ArrayWithDouble: {
696         unsigned length = butterfly->publicLength();
697         
698         if (!length--)
699             return jsUndefined();
700         
701         RELEASE_ASSERT(length < butterfly->vectorLength());
702         double value = butterfly->contiguousDouble().at(this, length);
703         if (value == value) {
704             butterfly->contiguousDouble().at(this, length) = PNaN;
705             butterfly->setPublicLength(length);
706             return JSValue(JSValue::EncodeAsDouble, value);
707         }
708         break;
709     }
710         
711     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
712         ArrayStorage* storage = butterfly->arrayStorage();
713     
714         unsigned length = storage->length();
715         if (!length) {
716             if (!isLengthWritable())
717                 throwTypeError(exec, scope, ReadonlyPropertyWriteError);
718             return jsUndefined();
719         }
720
721         unsigned index = length - 1;
722         if (index < storage->vectorLength()) {
723             WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
724             if (valueSlot) {
725                 --storage->m_numValuesInVector;
726                 JSValue element = valueSlot.get();
727                 valueSlot.clear();
728             
729                 RELEASE_ASSERT(isLengthWritable());
730                 storage->setLength(index);
731                 return element;
732             }
733         }
734         break;
735     }
736         
737     default:
738         CRASH();
739         return JSValue();
740     }
741     
742     unsigned index = getArrayLength() - 1;
743     // Let element be the result of calling the [[Get]] internal method of O with argument indx.
744     JSValue element = get(exec, index);
745     RETURN_IF_EXCEPTION(scope, JSValue());
746     // Call the [[Delete]] internal method of O with arguments indx and true.
747     bool success = deletePropertyByIndex(this, exec, index);
748     RETURN_IF_EXCEPTION(scope, JSValue());
749     if (!success) {
750         throwTypeError(exec, scope, UnableToDeletePropertyError);
751         return jsUndefined();
752     }
753     // Call the [[Put]] internal method of O with arguments "length", indx, and true.
754     scope.release();
755     setLength(exec, index, true);
756     // Return element.
757     return element;
758 }
759
760 // Push & putIndex are almost identical, with two small differences.
761 //  - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
762 //  - pushing to an array of length 2^32-1 stores the property, but throws a range error.
763 NEVER_INLINE void JSArray::push(ExecState* exec, JSValue value)
764 {
765     pushInline(exec, value);
766 }
767
768 JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
769 {
770     VM& vm = exec.vm();
771
772     ensureWritable(vm);
773
774     auto arrayType = indexingMode();
775     switch (arrayType) {
776     case ArrayWithDouble:
777     case ArrayWithInt32:
778     case ArrayWithContiguous: {
779         if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm, this))
780             return nullptr;
781
782         JSGlobalObject* lexicalGlobalObject = exec.lexicalGlobalObject();
783         Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(arrayType);
784         if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
785             return nullptr;
786
787         ASSERT(!lexicalGlobalObject->isHavingABadTime());
788         ObjectInitializationScope scope(vm);
789         JSArray* resultArray = JSArray::tryCreateUninitializedRestricted(scope, resultStructure, count);
790         if (UNLIKELY(!resultArray))
791             return nullptr;
792
793         auto& resultButterfly = *resultArray->butterfly();
794         if (arrayType == ArrayWithDouble)
795             memcpy(resultButterfly.contiguousDouble().data(), butterfly()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
796         else
797             memcpy(resultButterfly.contiguous().data(), butterfly()->contiguous().data() + startIndex, sizeof(JSValue) * count);
798         resultButterfly.setPublicLength(count);
799
800         return resultArray;
801     }
802     default:
803         return nullptr;
804     }
805 }
806
807 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
808 {
809     unsigned oldLength = storage->length();
810     RELEASE_ASSERT(count <= oldLength);
811     
812     // If the array contains holes or is otherwise in an abnormal state,
813     // use the generic algorithm in ArrayPrototype.
814     if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm, this)) 
815         || hasSparseMap() 
816         || shouldUseSlowPut(indexingType())) {
817         return false;
818     }
819
820     if (!oldLength)
821         return true;
822     
823     unsigned length = oldLength - count;
824     
825     storage->m_numValuesInVector -= count;
826     storage->setLength(length);
827     
828     unsigned vectorLength = storage->vectorLength();
829     if (!vectorLength)
830         return true;
831     
832     if (startIndex >= vectorLength)
833         return true;
834     
835     DisallowGC disallowGC;
836     auto locker = holdLock(cellLock());
837     
838     if (startIndex + count > vectorLength)
839         count = vectorLength - startIndex;
840     
841     unsigned usedVectorLength = std::min(vectorLength, oldLength);
842     
843     unsigned numElementsBeforeShiftRegion = startIndex;
844     unsigned firstIndexAfterShiftRegion = startIndex + count;
845     unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
846     ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
847
848     // The point of this comparison seems to be to minimize the amount of elements that have to 
849     // be moved during a shift operation.
850     if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
851         // The number of elements before the shift region is less than the number of elements
852         // after the shift region, so we move the elements before to the right.
853         if (numElementsBeforeShiftRegion) {
854             RELEASE_ASSERT(count + startIndex <= vectorLength);
855             if (storage->hasHoles()) {
856                 for (unsigned i = startIndex; i-- > 0;) {
857                     unsigned destinationIndex = count + i;
858                     JSValue source = storage->m_vector[i].get();
859                     JSValue dest = storage->m_vector[destinationIndex].get();
860                     // Any time we overwrite a hole we know we overcounted the number of values we removed 
861                     // when we subtracted count from m_numValuesInVector above.
862                     if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
863                         storage->m_numValuesInVector++;
864                     storage->m_vector[count + i].setWithoutWriteBarrier(source);
865                 }
866             } else {
867                 memmove(storage->m_vector + count,
868                     storage->m_vector,
869                     sizeof(JSValue) * startIndex);
870             }
871         }
872         // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
873         // the start of the Butterfly, which needs to point at the first indexed property in the used
874         // portion of the vector.
875         Butterfly* butterfly = this->butterfly()->shift(structure(vm), count);
876         storage = butterfly->arrayStorage();
877         storage->m_indexBias += count;
878
879         // Since we're consuming part of the vector by moving its beginning to the left,
880         // we need to modify the vector length appropriately.
881         storage->setVectorLength(vectorLength - count);
882         setButterfly(vm, butterfly);
883     } else {
884         // The number of elements before the shift region is greater than or equal to the number 
885         // of elements after the shift region, so we move the elements after the shift region to the left.
886         if (storage->hasHoles()) {
887             for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
888                 unsigned destinationIndex = startIndex + i;
889                 JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
890                 JSValue dest = storage->m_vector[destinationIndex].get();
891                 // Any time we overwrite a hole we know we overcounted the number of values we removed 
892                 // when we subtracted count from m_numValuesInVector above.
893                 if (!dest && destinationIndex < firstIndexAfterShiftRegion)
894                     storage->m_numValuesInVector++;
895                 storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
896             }
897         } else {
898             memmove(storage->m_vector + startIndex,
899                 storage->m_vector + firstIndexAfterShiftRegion,
900                 sizeof(JSValue) * numElementsAfterShiftRegion);
901         }
902         // Clear the slots of the elements we just moved.
903         unsigned startOfEmptyVectorTail = usedVectorLength - count;
904         for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
905             storage->m_vector[i].clear();
906         // We don't modify the index bias or the Butterfly pointer in this case because we're not changing 
907         // the start of the Butterfly, which needs to point at the first indexed property in the used 
908         // portion of the vector. We also don't modify the vector length because we're not actually changing
909         // its length; we're just using less of it.
910     }
911     
912     return true;
913 }
914
915 bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
916 {
917     VM& vm = exec->vm();
918     RELEASE_ASSERT(count > 0);
919
920     ensureWritable(vm);
921
922     Butterfly* butterfly = this->butterfly();
923     
924     switch (indexingType()) {
925     case ArrayClass:
926         return true;
927         
928     case ArrayWithUndecided:
929         // Don't handle this because it's confusing and it shouldn't come up.
930         return false;
931         
932     case ArrayWithInt32:
933     case ArrayWithContiguous: {
934         unsigned oldLength = butterfly->publicLength();
935         RELEASE_ASSERT(count <= oldLength);
936         
937         // We may have to walk the entire array to do the shift. We're willing to do
938         // so only if it's not horribly slow.
939         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
940             return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
941
942         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
943         // is totally not fine, since we might have to read from the proto chain.
944         // We have to check for holes before we start moving things around so that we don't get halfway 
945         // through shifting and then realize we should have been in ArrayStorage mode.
946         unsigned end = oldLength - count;
947         if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
948             for (unsigned i = startIndex; i < end; ++i) {
949                 JSValue v = butterfly->contiguous().at(this, i + count).get();
950                 if (UNLIKELY(!v)) {
951                     startIndex = i;
952                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
953                 }
954                 butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v);
955             }
956         } else {
957             memmove(butterfly->contiguous().data() + startIndex, 
958                 butterfly->contiguous().data() + startIndex + count, 
959                 sizeof(JSValue) * (end - startIndex));
960         }
961
962         for (unsigned i = end; i < oldLength; ++i)
963             butterfly->contiguous().at(this, i).clear();
964         
965         butterfly->setPublicLength(oldLength - count);
966
967         // Our memmoving of values around in the array could have concealed some of them from
968         // the collector. Let's make sure that the collector scans this object again.
969         vm.heap.writeBarrier(this);
970         
971         return true;
972     }
973         
974     case ArrayWithDouble: {
975         unsigned oldLength = butterfly->publicLength();
976         RELEASE_ASSERT(count <= oldLength);
977         
978         // We may have to walk the entire array to do the shift. We're willing to do
979         // so only if it's not horribly slow.
980         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
981             return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
982
983         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
984         // is totally not fine, since we might have to read from the proto chain.
985         // We have to check for holes before we start moving things around so that we don't get halfway 
986         // through shifting and then realize we should have been in ArrayStorage mode.
987         unsigned end = oldLength - count;
988         if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
989             for (unsigned i = startIndex; i < end; ++i) {
990                 double v = butterfly->contiguousDouble().at(this, i + count);
991                 if (UNLIKELY(v != v)) {
992                     startIndex = i;
993                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
994                 }
995                 butterfly->contiguousDouble().at(this, i) = v;
996             }
997         } else {
998             memmove(butterfly->contiguousDouble().data() + startIndex,
999                 butterfly->contiguousDouble().data() + startIndex + count,
1000                 sizeof(JSValue) * (end - startIndex));
1001         }
1002         for (unsigned i = end; i < oldLength; ++i)
1003             butterfly->contiguousDouble().at(this, i) = PNaN;
1004         
1005         butterfly->setPublicLength(oldLength - count);
1006         return true;
1007     }
1008         
1009     case ArrayWithArrayStorage:
1010     case ArrayWithSlowPutArrayStorage:
1011         return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
1012         
1013     default:
1014         CRASH();
1015         return false;
1016     }
1017 }
1018
1019 // Returns true if the unshift can be handled, false to fallback.    
1020 bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
1021 {
1022     VM& vm = exec->vm();
1023     auto scope = DECLARE_THROW_SCOPE(vm);
1024
1025     unsigned length = storage->length();
1026
1027     RELEASE_ASSERT(startIndex <= length);
1028
1029     // If the array contains holes or is otherwise in an abnormal state,
1030     // use the generic algorithm in ArrayPrototype.
1031     if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
1032         return false;
1033
1034     bool moveFront = !startIndex || startIndex < length / 2;
1035
1036     unsigned vectorLength = storage->vectorLength();
1037
1038     // Need to have GC deferred around the unshiftCountSlowCase(), since that leaves the butterfly in
1039     // a weird state: some parts of it will be left uninitialized, which we will fill in here.
1040     DeferGC deferGC(vm.heap);
1041     auto locker = holdLock(cellLock());
1042     
1043     if (moveFront && storage->m_indexBias >= count) {
1044         Butterfly* newButterfly = storage->butterfly()->unshift(structure(vm), count);
1045         storage = newButterfly->arrayStorage();
1046         storage->m_indexBias -= count;
1047         storage->setVectorLength(vectorLength + count);
1048         setButterfly(vm, newButterfly);
1049     } else if (!moveFront && vectorLength - length >= count)
1050         storage = storage->butterfly()->arrayStorage();
1051     else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count))
1052         storage = arrayStorage();
1053     else {
1054         throwOutOfMemoryError(exec, scope);
1055         return true;
1056     }
1057
1058     WriteBarrier<Unknown>* vector = storage->m_vector;
1059
1060     if (startIndex) {
1061         if (moveFront)
1062             memmove(vector, vector + count, startIndex * sizeof(JSValue));
1063         else if (length - startIndex)
1064             memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
1065     }
1066
1067     for (unsigned i = 0; i < count; i++)
1068         vector[i + startIndex].clear();
1069     
1070     return true;
1071 }
1072
1073 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
1074 {
1075     VM& vm = exec->vm();
1076     auto scope = DECLARE_THROW_SCOPE(vm);
1077
1078     ensureWritable(vm);
1079
1080     Butterfly* butterfly = this->butterfly();
1081     
1082     switch (indexingType()) {
1083     case ArrayClass:
1084     case ArrayWithUndecided:
1085         // We could handle this. But it shouldn't ever come up, so we won't.
1086         return false;
1087
1088     case ArrayWithInt32:
1089     case ArrayWithContiguous: {
1090         unsigned oldLength = butterfly->publicLength();
1091         
1092         // We may have to walk the entire array to do the unshift. We're willing to do so
1093         // only if it's not horribly slow.
1094         if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
1095             scope.release();
1096             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1097         }
1098
1099         Checked<unsigned, RecordOverflow> checkedLength(oldLength);
1100         checkedLength += count;
1101         unsigned newLength;
1102         if (CheckedState::DidOverflow == checkedLength.safeGet(newLength)) {
1103             throwOutOfMemoryError(exec, scope);
1104             return true;
1105         }
1106         if (newLength > MAX_STORAGE_VECTOR_LENGTH)
1107             return false;
1108         if (!ensureLength(vm, newLength)) {
1109             throwOutOfMemoryError(exec, scope);
1110             return true;
1111         }
1112         butterfly = this->butterfly();
1113
1114         // We have to check for holes before we start moving things around so that we don't get halfway 
1115         // through shifting and then realize we should have been in ArrayStorage mode.
1116         for (unsigned i = oldLength; i-- > startIndex;) {
1117             JSValue v = butterfly->contiguous().at(this, i).get();
1118             if (UNLIKELY(!v)) {
1119                 scope.release();
1120                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1121             }
1122         }
1123
1124         for (unsigned i = oldLength; i-- > startIndex;) {
1125             JSValue v = butterfly->contiguous().at(this, i).get();
1126             ASSERT(v);
1127             butterfly->contiguous().at(this, i + count).setWithoutWriteBarrier(v);
1128         }
1129         
1130         // Our memmoving of values around in the array could have concealed some of them from
1131         // the collector. Let's make sure that the collector scans this object again.
1132         vm.heap.writeBarrier(this);
1133         
1134         // NOTE: we're leaving being garbage in the part of the array that we shifted out
1135         // of. This is fine because the caller is required to store over that area, and
1136         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1137         // as storing over an existing element.
1138         
1139         return true;
1140     }
1141         
1142     case ArrayWithDouble: {
1143         unsigned oldLength = butterfly->publicLength();
1144         
1145         // We may have to walk the entire array to do the unshift. We're willing to do so
1146         // only if it's not horribly slow.
1147         if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
1148             scope.release();
1149             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1150         }
1151
1152         Checked<unsigned, RecordOverflow> checkedLength(oldLength);
1153         checkedLength += count;
1154         unsigned newLength;
1155         if (CheckedState::DidOverflow == checkedLength.safeGet(newLength)) {
1156             throwOutOfMemoryError(exec, scope);
1157             return true;
1158         }
1159         if (newLength > MAX_STORAGE_VECTOR_LENGTH)
1160             return false;
1161         if (!ensureLength(vm, newLength)) {
1162             throwOutOfMemoryError(exec, scope);
1163             return true;
1164         }
1165         butterfly = this->butterfly();
1166         
1167         // We have to check for holes before we start moving things around so that we don't get halfway 
1168         // through shifting and then realize we should have been in ArrayStorage mode.
1169         for (unsigned i = oldLength; i-- > startIndex;) {
1170             double v = butterfly->contiguousDouble().at(this, i);
1171             if (UNLIKELY(v != v)) {
1172                 scope.release();
1173                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
1174             }
1175         }
1176
1177         for (unsigned i = oldLength; i-- > startIndex;) {
1178             double v = butterfly->contiguousDouble().at(this, i);
1179             ASSERT(v == v);
1180             butterfly->contiguousDouble().at(this, i + count) = v;
1181         }
1182         
1183         // NOTE: we're leaving being garbage in the part of the array that we shifted out
1184         // of. This is fine because the caller is required to store over that area, and
1185         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1186         // as storing over an existing element.
1187         
1188         return true;
1189     }
1190         
1191     case ArrayWithArrayStorage:
1192     case ArrayWithSlowPutArrayStorage:
1193         scope.release();
1194         return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
1195         
1196     default:
1197         CRASH();
1198         return false;
1199     }
1200 }
1201
1202 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1203 {
1204     unsigned i = 0;
1205     unsigned vectorEnd;
1206     WriteBarrier<Unknown>* vector;
1207
1208     Butterfly* butterfly = this->butterfly();
1209     
1210     switch (indexingType()) {
1211     case ArrayClass:
1212         return;
1213         
1214     case ArrayWithUndecided: {
1215         vector = 0;
1216         vectorEnd = 0;
1217         break;
1218     }
1219         
1220     case ArrayWithInt32:
1221     case ArrayWithContiguous: {
1222         vectorEnd = butterfly->publicLength();
1223         vector = butterfly->contiguous().data();
1224         break;
1225     }
1226         
1227     case ArrayWithDouble: {
1228         vector = 0;
1229         vectorEnd = 0;
1230         for (; i < butterfly->publicLength(); ++i) {
1231             double v = butterfly->contiguousDouble().at(this, i);
1232             if (v != v)
1233                 break;
1234             args.append(JSValue(JSValue::EncodeAsDouble, v));
1235         }
1236         break;
1237     }
1238     
1239     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1240         ArrayStorage* storage = butterfly->arrayStorage();
1241         
1242         vector = storage->m_vector;
1243         vectorEnd = std::min(storage->length(), storage->vectorLength());
1244         break;
1245     }
1246         
1247     default:
1248         CRASH();
1249 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1250         vector = 0;
1251         vectorEnd = 0;
1252         break;
1253 #endif
1254     }
1255     
1256     for (; i < vectorEnd; ++i) {
1257         WriteBarrier<Unknown>& v = vector[i];
1258         if (!v)
1259             break;
1260         args.append(v.get());
1261     }
1262
1263     // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1264     for (; i < length(); ++i)
1265         args.append(get(exec, i));
1266 }
1267
1268 void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
1269 {
1270     VM& vm = exec->vm();
1271     auto scope = DECLARE_THROW_SCOPE(vm);
1272
1273     unsigned i = offset;
1274     WriteBarrier<Unknown>* vector;
1275     unsigned vectorEnd;
1276     length += offset; // We like to think of the length as being our length, rather than the output length.
1277
1278     // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1279     ASSERT(length == this->length());
1280
1281     Butterfly* butterfly = this->butterfly();
1282     switch (indexingType()) {
1283     case ArrayClass:
1284         return;
1285         
1286     case ArrayWithUndecided: {
1287         vector = 0;
1288         vectorEnd = 0;
1289         break;
1290     }
1291         
1292     case ArrayWithInt32:
1293     case ArrayWithContiguous: {
1294         vector = butterfly->contiguous().data();
1295         vectorEnd = butterfly->publicLength();
1296         break;
1297     }
1298         
1299     case ArrayWithDouble: {
1300         vector = 0;
1301         vectorEnd = 0;
1302         for (; i < butterfly->publicLength(); ++i) {
1303             ASSERT(i < butterfly->vectorLength());
1304             double v = butterfly->contiguousDouble().at(this, i);
1305             if (v != v)
1306                 break;
1307             exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
1308         }
1309         break;
1310     }
1311         
1312     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1313         ArrayStorage* storage = butterfly->arrayStorage();
1314         vector = storage->m_vector;
1315         vectorEnd = std::min(length, storage->vectorLength());
1316         break;
1317     }
1318         
1319     default:
1320         CRASH();
1321 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1322         vector = 0;
1323         vectorEnd = 0;
1324         break;
1325 #endif
1326     }
1327     
1328     for (; i < vectorEnd; ++i) {
1329         WriteBarrier<Unknown>& v = vector[i];
1330         if (!v)
1331             break;
1332         exec->r(firstElementDest + i - offset) = v.get();
1333     }
1334     
1335     for (; i < length; ++i) {
1336         exec->r(firstElementDest + i - offset) = get(exec, i);
1337         RETURN_IF_EXCEPTION(scope, void());
1338     }
1339 }
1340
1341 bool JSArray::isIteratorProtocolFastAndNonObservable()
1342 {
1343     JSGlobalObject* globalObject = this->globalObject();
1344     if (!globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
1345         return false;
1346
1347     VM& vm = globalObject->vm();
1348     Structure* structure = this->structure(vm);
1349     // This is the fast case. Many arrays will be an original array.
1350     if (globalObject->isOriginalArrayStructure(structure))
1351         return true;
1352
1353     if (structure->mayInterceptIndexedAccesses())
1354         return false;
1355
1356     if (getPrototypeDirect(vm) != globalObject->arrayPrototype())
1357         return false;
1358
1359     if (getDirectOffset(vm, vm.propertyNames->iteratorSymbol) != invalidOffset)
1360         return false;
1361
1362     return true;
1363 }
1364
1365 inline JSArray* constructArray(ObjectInitializationScope& scope, Structure* arrayStructure, unsigned length)
1366 {
1367     JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
1368
1369     // FIXME: we should probably throw an out of memory error here, but
1370     // when making this change we should check that all clients of this
1371     // function will correctly handle an exception being thrown from here.
1372     // https://bugs.webkit.org/show_bug.cgi?id=169786
1373     RELEASE_ASSERT(array);
1374
1375     // FIXME: We only need this for subclasses of Array because we might need to allocate a new structure to change
1376     // indexing types while initializing. If this triggered a GC then we might scan our currently uninitialized
1377     // array and crash. https://bugs.webkit.org/show_bug.cgi?id=186811
1378     if (!arrayStructure->globalObject()->isOriginalArrayStructure(arrayStructure))
1379         JSArray::eagerlyInitializeButterfly(scope, array, length);
1380
1381     return array;
1382 }
1383
1384 JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
1385 {
1386     VM& vm = exec->vm();
1387     unsigned length = values.size();
1388     ObjectInitializationScope scope(vm);
1389
1390     JSArray* array = constructArray(scope, arrayStructure, length);
1391     for (unsigned i = 0; i < length; ++i)
1392         array->initializeIndex(scope, i, values.at(i));
1393     return array;
1394 }
1395
1396 JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
1397 {
1398     VM& vm = exec->vm();
1399     ObjectInitializationScope scope(vm);
1400
1401     JSArray* array = constructArray(scope, arrayStructure, length);
1402     for (unsigned i = 0; i < length; ++i)
1403         array->initializeIndex(scope, i, values[i]);
1404     return array;
1405 }
1406
1407 JSArray* constructArrayNegativeIndexed(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
1408 {
1409     VM& vm = exec->vm();
1410     ObjectInitializationScope scope(vm);
1411
1412     JSArray* array = constructArray(scope, arrayStructure, length);
1413     for (int i = 0; i < static_cast<int>(length); ++i)
1414         array->initializeIndex(scope, i, values[-i]);
1415     return array;
1416 }
1417
1418 } // namespace JSC