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