afa062f903b606e5b7b58ba3cbe0f53a46cdb0bb
[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::setLength(ExecState* exec, unsigned newLength, bool throwException)
396 {
397     Butterfly* butterfly = m_butterfly.get();
398     switch (indexingType()) {
399     case ArrayClass:
400         if (!newLength)
401             return true;
402         if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
403             return setLengthWithArrayStorage(
404                 exec, newLength, throwException,
405                 convertContiguousToArrayStorage(exec->vm()));
406         }
407         createInitialUndecided(exec->vm(), newLength);
408         return true;
409         
410     case ArrayWithUndecided:
411     case ArrayWithInt32:
412     case ArrayWithDouble:
413     case ArrayWithContiguous: {
414         if (newLength == butterfly->publicLength())
415             return true;
416         if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push.
417             || (newLength >= MIN_SPARSE_ARRAY_INDEX
418                 && !isDenseEnoughForVector(newLength, countElements()))) {
419             return setLengthWithArrayStorage(
420                 exec, newLength, throwException,
421                 ensureArrayStorage(exec->vm()));
422         }
423         if (newLength > butterfly->publicLength()) {
424             if (!ensureLength(exec->vm(), newLength)) {
425                 throwOutOfMemoryError(exec);
426                 return false;
427             }
428             return true;
429         }
430
431         unsigned lengthToClear = butterfly->publicLength() - newLength;
432         unsigned costToAllocateNewButterfly = 64; // a heuristic.
433         if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
434             reallocateAndShrinkButterfly(exec->vm(), newLength);
435             return true;
436         }
437
438         if (indexingType() == ArrayWithDouble) {
439             for (unsigned i = butterfly->publicLength(); i-- > newLength;)
440                 butterfly->contiguousDouble()[i] = PNaN;
441         } else {
442             for (unsigned i = butterfly->publicLength(); i-- > newLength;)
443                 butterfly->contiguous()[i].clear();
444         }
445         butterfly->setPublicLength(newLength);
446         return true;
447     }
448         
449     case ArrayWithArrayStorage:
450     case ArrayWithSlowPutArrayStorage:
451         return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
452         
453     default:
454         CRASH();
455         return false;
456     }
457 }
458
459 JSValue JSArray::pop(ExecState* exec)
460 {
461     Butterfly* butterfly = m_butterfly.get();
462     
463     switch (indexingType()) {
464     case ArrayClass:
465         return jsUndefined();
466         
467     case ArrayWithUndecided:
468         if (!butterfly->publicLength())
469             return jsUndefined();
470         // We have nothing but holes. So, drop down to the slow version.
471         break;
472         
473     case ArrayWithInt32:
474     case ArrayWithContiguous: {
475         unsigned length = butterfly->publicLength();
476         
477         if (!length--)
478             return jsUndefined();
479         
480         RELEASE_ASSERT(length < butterfly->vectorLength());
481         JSValue value = butterfly->contiguous()[length].get();
482         if (value) {
483             butterfly->contiguous()[length].clear();
484             butterfly->setPublicLength(length);
485             return value;
486         }
487         break;
488     }
489         
490     case ArrayWithDouble: {
491         unsigned length = butterfly->publicLength();
492         
493         if (!length--)
494             return jsUndefined();
495         
496         RELEASE_ASSERT(length < butterfly->vectorLength());
497         double value = butterfly->contiguousDouble()[length];
498         if (value == value) {
499             butterfly->contiguousDouble()[length] = PNaN;
500             butterfly->setPublicLength(length);
501             return JSValue(JSValue::EncodeAsDouble, value);
502         }
503         break;
504     }
505         
506     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
507         ArrayStorage* storage = butterfly->arrayStorage();
508     
509         unsigned length = storage->length();
510         if (!length) {
511             if (!isLengthWritable())
512                 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
513             return jsUndefined();
514         }
515
516         unsigned index = length - 1;
517         if (index < storage->vectorLength()) {
518             WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
519             if (valueSlot) {
520                 --storage->m_numValuesInVector;
521                 JSValue element = valueSlot.get();
522                 valueSlot.clear();
523             
524                 RELEASE_ASSERT(isLengthWritable());
525                 storage->setLength(index);
526                 return element;
527             }
528         }
529         break;
530     }
531         
532     default:
533         CRASH();
534         return JSValue();
535     }
536     
537     unsigned index = getArrayLength() - 1;
538     // Let element be the result of calling the [[Get]] internal method of O with argument indx.
539     JSValue element = get(exec, index);
540     if (exec->hadException())
541         return jsUndefined();
542     // Call the [[Delete]] internal method of O with arguments indx and true.
543     if (!deletePropertyByIndex(this, exec, index)) {
544         throwTypeError(exec, ASCIILiteral("Unable to delete property."));
545         return jsUndefined();
546     }
547     // Call the [[Put]] internal method of O with arguments "length", indx, and true.
548     setLength(exec, index, true);
549     // Return element.
550     return element;
551 }
552
553 // Push & putIndex are almost identical, with two small differences.
554 //  - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
555 //  - pushing to an array of length 2^32-1 stores the property, but throws a range error.
556 void JSArray::push(ExecState* exec, JSValue value)
557 {
558     Butterfly* butterfly = m_butterfly.get();
559     
560     switch (indexingType()) {
561     case ArrayClass: {
562         createInitialUndecided(exec->vm(), 0);
563         FALLTHROUGH;
564     }
565         
566     case ArrayWithUndecided: {
567         convertUndecidedForValue(exec->vm(), value);
568         push(exec, value);
569         return;
570     }
571         
572     case ArrayWithInt32: {
573         if (!value.isInt32()) {
574             convertInt32ForValue(exec->vm(), value);
575             push(exec, value);
576             return;
577         }
578
579         unsigned length = butterfly->publicLength();
580         ASSERT(length <= butterfly->vectorLength());
581         if (length < butterfly->vectorLength()) {
582             butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
583             butterfly->setPublicLength(length + 1);
584             return;
585         }
586         
587         if (length > MAX_ARRAY_INDEX) {
588             methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
589             if (!exec->hadException())
590                 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
591             return;
592         }
593         
594         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
595         return;
596     }
597
598     case ArrayWithContiguous: {
599         unsigned length = butterfly->publicLength();
600         ASSERT(length <= butterfly->vectorLength());
601         if (length < butterfly->vectorLength()) {
602             butterfly->contiguous()[length].set(exec->vm(), this, value);
603             butterfly->setPublicLength(length + 1);
604             return;
605         }
606         
607         if (length > MAX_ARRAY_INDEX) {
608             methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
609             if (!exec->hadException())
610                 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
611             return;
612         }
613         
614         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
615         return;
616     }
617         
618     case ArrayWithDouble: {
619         if (!value.isNumber()) {
620             convertDoubleToContiguous(exec->vm());
621             push(exec, value);
622             return;
623         }
624         double valueAsDouble = value.asNumber();
625         if (valueAsDouble != valueAsDouble) {
626             convertDoubleToContiguous(exec->vm());
627             push(exec, value);
628             return;
629         }
630
631         unsigned length = butterfly->publicLength();
632         ASSERT(length <= butterfly->vectorLength());
633         if (length < butterfly->vectorLength()) {
634             butterfly->contiguousDouble()[length] = valueAsDouble;
635             butterfly->setPublicLength(length + 1);
636             return;
637         }
638         
639         if (length > MAX_ARRAY_INDEX) {
640             methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
641             if (!exec->hadException())
642                 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
643             return;
644         }
645         
646         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
647         break;
648     }
649         
650     case ArrayWithSlowPutArrayStorage: {
651         unsigned oldLength = length();
652         bool putResult = false;
653         if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult)) {
654             if (!exec->hadException() && oldLength < 0xFFFFFFFFu)
655                 setLength(exec, oldLength + 1, true);
656             return;
657         }
658         FALLTHROUGH;
659     }
660         
661     case ArrayWithArrayStorage: {
662         ArrayStorage* storage = butterfly->arrayStorage();
663
664         // Fast case - push within vector, always update m_length & m_numValuesInVector.
665         unsigned length = storage->length();
666         if (length < storage->vectorLength()) {
667             storage->m_vector[length].set(exec->vm(), this, value);
668             storage->setLength(length + 1);
669             ++storage->m_numValuesInVector;
670             return;
671         }
672
673         // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
674         if (storage->length() > MAX_ARRAY_INDEX) {
675             methodTable(exec->vm())->putByIndex(this, exec, storage->length(), value, true);
676             // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
677             if (!exec->hadException())
678                 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
679             return;
680         }
681
682         // Handled the same as putIndex.
683         putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
684         break;
685     }
686         
687     default:
688         RELEASE_ASSERT_NOT_REACHED();
689     }
690 }
691
692 JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
693 {
694     auto arrayType = indexingType();
695     switch (arrayType) {
696     case ArrayWithDouble:
697     case ArrayWithInt32:
698     case ArrayWithContiguous: {
699         VM& vm = exec.vm();
700         if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm))
701             return nullptr;
702
703         Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(arrayType);
704         JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, count);
705         if (!resultArray)
706             return nullptr;
707
708         auto& resultButterfly = *resultArray->butterfly();
709         if (arrayType == ArrayWithDouble)
710             memcpy(resultButterfly.contiguousDouble().data(), m_butterfly.get()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
711         else
712             memcpy(resultButterfly.contiguous().data(), m_butterfly.get()->contiguous().data() + startIndex, sizeof(JSValue) * count);
713         resultButterfly.setPublicLength(count);
714
715         return resultArray;
716     }
717     default:
718         return nullptr;
719     }
720 }
721
722 EncodedJSValue JSArray::fastConcatWith(ExecState& exec, JSArray& otherArray)
723 {
724     auto newArrayType = indexingType();
725
726     VM& vm = exec.vm();
727     ASSERT(newArrayType == fastConcatType(vm, *this, otherArray));
728
729     unsigned thisArraySize = m_butterfly.get()->publicLength();
730     unsigned otherArraySize = otherArray.m_butterfly.get()->publicLength();
731     ASSERT(thisArraySize + otherArraySize < MIN_SPARSE_ARRAY_INDEX);
732
733     Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(newArrayType);
734     JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, thisArraySize + otherArraySize);
735     if (!resultArray)
736         return JSValue::encode(throwOutOfMemoryError(&exec));
737
738     auto& resultButterfly = *resultArray->butterfly();
739     auto& otherButterfly = *otherArray.butterfly();
740     if (newArrayType == ArrayWithDouble) {
741         auto buffer = resultButterfly.contiguousDouble().data();
742         memcpy(buffer, m_butterfly.get()->contiguousDouble().data(), sizeof(JSValue) * thisArraySize);
743         memcpy(buffer + thisArraySize, otherButterfly.contiguousDouble().data(), sizeof(JSValue) * otherArraySize);
744     } else {
745         auto buffer = resultButterfly.contiguous().data();
746         memcpy(buffer, m_butterfly.get()->contiguous().data(), sizeof(JSValue) * thisArraySize);
747         memcpy(buffer + thisArraySize, otherButterfly.contiguous().data(), sizeof(JSValue) * otherArraySize);
748     }
749
750     resultButterfly.setPublicLength(thisArraySize + otherArraySize);
751     return JSValue::encode(resultArray);
752 }
753
754 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
755 {
756     unsigned oldLength = storage->length();
757     RELEASE_ASSERT(count <= oldLength);
758     
759     // If the array contains holes or is otherwise in an abnormal state,
760     // use the generic algorithm in ArrayPrototype.
761     if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm)) 
762         || hasSparseMap() 
763         || shouldUseSlowPut(indexingType())) {
764         return false;
765     }
766
767     if (!oldLength)
768         return true;
769     
770     unsigned length = oldLength - count;
771     
772     storage->m_numValuesInVector -= count;
773     storage->setLength(length);
774     
775     unsigned vectorLength = storage->vectorLength();
776     if (!vectorLength)
777         return true;
778     
779     if (startIndex >= vectorLength)
780         return true;
781     
782     if (startIndex + count > vectorLength)
783         count = vectorLength - startIndex;
784     
785     unsigned usedVectorLength = min(vectorLength, oldLength);
786     
787     unsigned numElementsBeforeShiftRegion = startIndex;
788     unsigned firstIndexAfterShiftRegion = startIndex + count;
789     unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
790     ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
791
792     // The point of this comparison seems to be to minimize the amount of elements that have to 
793     // be moved during a shift operation.
794     if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
795         // The number of elements before the shift region is less than the number of elements
796         // after the shift region, so we move the elements before to the right.
797         if (numElementsBeforeShiftRegion) {
798             RELEASE_ASSERT(count + startIndex <= vectorLength);
799             if (storage->hasHoles()) {
800                 for (unsigned i = startIndex; i-- > 0;) {
801                     unsigned destinationIndex = count + i;
802                     JSValue source = storage->m_vector[i].get();
803                     JSValue dest = storage->m_vector[destinationIndex].get();
804                     // Any time we overwrite a hole we know we overcounted the number of values we removed 
805                     // when we subtracted count from m_numValuesInVector above.
806                     if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
807                         storage->m_numValuesInVector++;
808                     storage->m_vector[count + i].setWithoutWriteBarrier(source);
809                 }
810             } else {
811                 memmove(storage->m_vector + count,
812                     storage->m_vector,
813                     sizeof(JSValue) * startIndex);
814             }
815         }
816         // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
817         // the start of the Butterfly, which needs to point at the first indexed property in the used
818         // portion of the vector.
819         Butterfly* butterfly = m_butterfly.get()->shift(structure(), count);
820         m_butterfly.setWithoutBarrier(butterfly);
821         storage = butterfly->arrayStorage();
822         storage->m_indexBias += count;
823
824         // Since we're consuming part of the vector by moving its beginning to the left,
825         // we need to modify the vector length appropriately.
826         storage->setVectorLength(vectorLength - count);
827     } else {
828         // The number of elements before the shift region is greater than or equal to the number 
829         // of elements after the shift region, so we move the elements after the shift region to the left.
830         if (storage->hasHoles()) {
831             for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
832                 unsigned destinationIndex = startIndex + i;
833                 JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
834                 JSValue dest = storage->m_vector[destinationIndex].get();
835                 // Any time we overwrite a hole we know we overcounted the number of values we removed 
836                 // when we subtracted count from m_numValuesInVector above.
837                 if (!dest && destinationIndex < firstIndexAfterShiftRegion)
838                     storage->m_numValuesInVector++;
839                 storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
840             }
841         } else {
842             memmove(storage->m_vector + startIndex,
843                 storage->m_vector + firstIndexAfterShiftRegion,
844                 sizeof(JSValue) * numElementsAfterShiftRegion);
845         }
846         // Clear the slots of the elements we just moved.
847         unsigned startOfEmptyVectorTail = usedVectorLength - count;
848         for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
849             storage->m_vector[i].clear();
850         // We don't modify the index bias or the Butterfly pointer in this case because we're not changing 
851         // the start of the Butterfly, which needs to point at the first indexed property in the used 
852         // portion of the vector. We also don't modify the vector length because we're not actually changing
853         // its length; we're just using less of it.
854     }
855     
856     return true;
857 }
858
859 bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
860 {
861     VM& vm = exec->vm();
862     RELEASE_ASSERT(count > 0);
863
864     Butterfly* butterfly = m_butterfly.get();
865     
866     switch (indexingType()) {
867     case ArrayClass:
868         return true;
869         
870     case ArrayWithUndecided:
871         // Don't handle this because it's confusing and it shouldn't come up.
872         return false;
873         
874     case ArrayWithInt32:
875     case ArrayWithContiguous: {
876         unsigned oldLength = butterfly->publicLength();
877         RELEASE_ASSERT(count <= oldLength);
878         
879         // We may have to walk the entire array to do the shift. We're willing to do
880         // so only if it's not horribly slow.
881         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
882             return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
883
884         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
885         // is totally not fine, since we might have to read from the proto chain.
886         // We have to check for holes before we start moving things around so that we don't get halfway 
887         // through shifting and then realize we should have been in ArrayStorage mode.
888         unsigned end = oldLength - count;
889         if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
890             for (unsigned i = startIndex; i < end; ++i) {
891                 JSValue v = butterfly->contiguous()[i + count].get();
892                 if (UNLIKELY(!v)) {
893                     startIndex = i;
894                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
895                 }
896                 butterfly->contiguous()[i].setWithoutWriteBarrier(v);
897             }
898         } else {
899             memmove(butterfly->contiguous().data() + startIndex, 
900                 butterfly->contiguous().data() + startIndex + count, 
901                 sizeof(JSValue) * (end - startIndex));
902         }
903
904         for (unsigned i = end; i < oldLength; ++i)
905             butterfly->contiguous()[i].clear();
906         
907         butterfly->setPublicLength(oldLength - count);
908         return true;
909     }
910         
911     case ArrayWithDouble: {
912         unsigned oldLength = butterfly->publicLength();
913         RELEASE_ASSERT(count <= oldLength);
914         
915         // We may have to walk the entire array to do the shift. We're willing to do
916         // so only if it's not horribly slow.
917         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
918             return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
919
920         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
921         // is totally not fine, since we might have to read from the proto chain.
922         // We have to check for holes before we start moving things around so that we don't get halfway 
923         // through shifting and then realize we should have been in ArrayStorage mode.
924         unsigned end = oldLength - count;
925         if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
926             for (unsigned i = startIndex; i < end; ++i) {
927                 double v = butterfly->contiguousDouble()[i + count];
928                 if (UNLIKELY(v != v)) {
929                     startIndex = i;
930                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
931                 }
932                 butterfly->contiguousDouble()[i] = v;
933             }
934         } else {
935             memmove(butterfly->contiguousDouble().data() + startIndex,
936                 butterfly->contiguousDouble().data() + startIndex + count,
937                 sizeof(JSValue) * (end - startIndex));
938         }
939         for (unsigned i = end; i < oldLength; ++i)
940             butterfly->contiguousDouble()[i] = PNaN;
941         
942         butterfly->setPublicLength(oldLength - count);
943         return true;
944     }
945         
946     case ArrayWithArrayStorage:
947     case ArrayWithSlowPutArrayStorage:
948         return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
949         
950     default:
951         CRASH();
952         return false;
953     }
954 }
955
956 // Returns true if the unshift can be handled, false to fallback.    
957 bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
958 {
959     unsigned length = storage->length();
960
961     RELEASE_ASSERT(startIndex <= length);
962
963     // If the array contains holes or is otherwise in an abnormal state,
964     // use the generic algorithm in ArrayPrototype.
965     if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
966         return false;
967
968     bool moveFront = !startIndex || startIndex < length / 2;
969
970     unsigned vectorLength = storage->vectorLength();
971
972     if (moveFront && storage->m_indexBias >= count) {
973         Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
974         storage = newButterfly->arrayStorage();
975         storage->m_indexBias -= count;
976         storage->setVectorLength(vectorLength + count);
977         setButterflyWithoutChangingStructure(exec->vm(), newButterfly);
978     } else if (!moveFront && vectorLength - length >= count)
979         storage = storage->butterfly()->arrayStorage();
980     else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
981         storage = arrayStorage();
982     else {
983         throwOutOfMemoryError(exec);
984         return true;
985     }
986
987     WriteBarrier<Unknown>* vector = storage->m_vector;
988
989     if (startIndex) {
990         if (moveFront)
991             memmove(vector, vector + count, startIndex * sizeof(JSValue));
992         else if (length - startIndex)
993             memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
994     }
995
996     for (unsigned i = 0; i < count; i++)
997         vector[i + startIndex].clear();
998     return true;
999 }
1000
1001 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
1002 {
1003     Butterfly* butterfly = m_butterfly.get();
1004     
1005     switch (indexingType()) {
1006     case ArrayClass:
1007     case ArrayWithUndecided:
1008         // We could handle this. But it shouldn't ever come up, so we won't.
1009         return false;
1010
1011     case ArrayWithInt32:
1012     case ArrayWithContiguous: {
1013         unsigned oldLength = butterfly->publicLength();
1014         
1015         // We may have to walk the entire array to do the unshift. We're willing to do so
1016         // only if it's not horribly slow.
1017         if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
1018             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1019         
1020         if (!ensureLength(exec->vm(), oldLength + count)) {
1021             throwOutOfMemoryError(exec);
1022             return false;
1023         }
1024         butterfly = m_butterfly.get();
1025
1026         // We have to check for holes before we start moving things around so that we don't get halfway 
1027         // through shifting and then realize we should have been in ArrayStorage mode.
1028         for (unsigned i = oldLength; i-- > startIndex;) {
1029             JSValue v = butterfly->contiguous()[i].get();
1030             if (UNLIKELY(!v))
1031                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1032         }
1033
1034         for (unsigned i = oldLength; i-- > startIndex;) {
1035             JSValue v = butterfly->contiguous()[i].get();
1036             ASSERT(v);
1037             butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
1038         }
1039         
1040         // NOTE: we're leaving being garbage in the part of the array that we shifted out
1041         // of. This is fine because the caller is required to store over that area, and
1042         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1043         // as storing over an existing element.
1044         
1045         return true;
1046     }
1047         
1048     case ArrayWithDouble: {
1049         unsigned oldLength = butterfly->publicLength();
1050         
1051         // We may have to walk the entire array to do the unshift. We're willing to do so
1052         // only if it's not horribly slow.
1053         if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
1054             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1055         
1056         if (!ensureLength(exec->vm(), oldLength + count)) {
1057             throwOutOfMemoryError(exec);
1058             return false;
1059         }
1060         butterfly = m_butterfly.get();
1061         
1062         // We have to check for holes before we start moving things around so that we don't get halfway 
1063         // through shifting and then realize we should have been in ArrayStorage mode.
1064         for (unsigned i = oldLength; i-- > startIndex;) {
1065             double v = butterfly->contiguousDouble()[i];
1066             if (UNLIKELY(v != v))
1067                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1068         }
1069
1070         for (unsigned i = oldLength; i-- > startIndex;) {
1071             double v = butterfly->contiguousDouble()[i];
1072             ASSERT(v == v);
1073             butterfly->contiguousDouble()[i + count] = v;
1074         }
1075         
1076         // NOTE: we're leaving being garbage in the part of the array that we shifted out
1077         // of. This is fine because the caller is required to store over that area, and
1078         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1079         // as storing over an existing element.
1080         
1081         return true;
1082     }
1083         
1084     case ArrayWithArrayStorage:
1085     case ArrayWithSlowPutArrayStorage:
1086         return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
1087         
1088     default:
1089         CRASH();
1090         return false;
1091     }
1092 }
1093
1094 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1095 {
1096     unsigned i = 0;
1097     unsigned vectorEnd;
1098     WriteBarrier<Unknown>* vector;
1099
1100     Butterfly* butterfly = m_butterfly.get();
1101     
1102     switch (indexingType()) {
1103     case ArrayClass:
1104         return;
1105         
1106     case ArrayWithUndecided: {
1107         vector = 0;
1108         vectorEnd = 0;
1109         break;
1110     }
1111         
1112     case ArrayWithInt32:
1113     case ArrayWithContiguous: {
1114         vectorEnd = butterfly->publicLength();
1115         vector = butterfly->contiguous().data();
1116         break;
1117     }
1118         
1119     case ArrayWithDouble: {
1120         vector = 0;
1121         vectorEnd = 0;
1122         for (; i < butterfly->publicLength(); ++i) {
1123             double v = butterfly->contiguousDouble()[i];
1124             if (v != v)
1125                 break;
1126             args.append(JSValue(JSValue::EncodeAsDouble, v));
1127         }
1128         break;
1129     }
1130     
1131     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1132         ArrayStorage* storage = butterfly->arrayStorage();
1133         
1134         vector = storage->m_vector;
1135         vectorEnd = min(storage->length(), storage->vectorLength());
1136         break;
1137     }
1138         
1139     default:
1140         CRASH();
1141 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1142         vector = 0;
1143         vectorEnd = 0;
1144         break;
1145 #endif
1146     }
1147     
1148     for (; i < vectorEnd; ++i) {
1149         WriteBarrier<Unknown>& v = vector[i];
1150         if (!v)
1151             break;
1152         args.append(v.get());
1153     }
1154
1155     // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1156     for (; i < length(); ++i)
1157         args.append(get(exec, i));
1158 }
1159
1160 void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
1161 {
1162     unsigned i = offset;
1163     WriteBarrier<Unknown>* vector;
1164     unsigned vectorEnd;
1165     length += offset; // We like to think of the length as being our length, rather than the output length.
1166
1167     // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1168     ASSERT(length == this->length());
1169
1170     Butterfly* butterfly = m_butterfly.get();
1171     
1172     switch (indexingType()) {
1173     case ArrayClass:
1174         return;
1175         
1176     case ArrayWithUndecided: {
1177         vector = 0;
1178         vectorEnd = 0;
1179         break;
1180     }
1181         
1182     case ArrayWithInt32:
1183     case ArrayWithContiguous: {
1184         vector = butterfly->contiguous().data();
1185         vectorEnd = butterfly->publicLength();
1186         break;
1187     }
1188         
1189     case ArrayWithDouble: {
1190         vector = 0;
1191         vectorEnd = 0;
1192         for (; i < butterfly->publicLength(); ++i) {
1193             ASSERT(i < butterfly->vectorLength());
1194             double v = butterfly->contiguousDouble()[i];
1195             if (v != v)
1196                 break;
1197             exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
1198         }
1199         break;
1200     }
1201         
1202     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1203         ArrayStorage* storage = butterfly->arrayStorage();
1204         vector = storage->m_vector;
1205         vectorEnd = min(length, storage->vectorLength());
1206         break;
1207     }
1208         
1209     default:
1210         CRASH();
1211 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1212         vector = 0;
1213         vectorEnd = 0;
1214         break;
1215 #endif
1216     }
1217     
1218     for (; i < vectorEnd; ++i) {
1219         WriteBarrier<Unknown>& v = vector[i];
1220         if (!v)
1221             break;
1222         exec->r(firstElementDest + i - offset) = v.get();
1223     }
1224     
1225     for (; i < length; ++i) {
1226         exec->r(firstElementDest + i - offset) = get(exec, i);
1227         if (UNLIKELY(exec->vm().exception()))
1228             return;
1229     }
1230 }
1231
1232 } // namespace JSC