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