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