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