51914da67e8fb517ae1edf680b1399387d156eee
[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 bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
711 {
712     unsigned oldLength = storage->length();
713     RELEASE_ASSERT(count <= oldLength);
714     
715     // If the array contains holes or is otherwise in an abnormal state,
716     // use the generic algorithm in ArrayPrototype.
717     if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm)) 
718         || hasSparseMap() 
719         || shouldUseSlowPut(indexingType())) {
720         return false;
721     }
722
723     if (!oldLength)
724         return true;
725     
726     unsigned length = oldLength - count;
727     
728     storage->m_numValuesInVector -= count;
729     storage->setLength(length);
730     
731     unsigned vectorLength = storage->vectorLength();
732     if (!vectorLength)
733         return true;
734     
735     if (startIndex >= vectorLength)
736         return true;
737     
738     if (startIndex + count > vectorLength)
739         count = vectorLength - startIndex;
740     
741     unsigned usedVectorLength = min(vectorLength, oldLength);
742     
743     unsigned numElementsBeforeShiftRegion = startIndex;
744     unsigned firstIndexAfterShiftRegion = startIndex + count;
745     unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
746     ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
747
748     // The point of this comparison seems to be to minimize the amount of elements that have to 
749     // be moved during a shift operation.
750     if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
751         // The number of elements before the shift region is less than the number of elements
752         // after the shift region, so we move the elements before to the right.
753         if (numElementsBeforeShiftRegion) {
754             RELEASE_ASSERT(count + startIndex <= vectorLength);
755             if (storage->hasHoles()) {
756                 for (unsigned i = startIndex; i-- > 0;) {
757                     unsigned destinationIndex = count + i;
758                     JSValue source = storage->m_vector[i].get();
759                     JSValue dest = storage->m_vector[destinationIndex].get();
760                     // Any time we overwrite a hole we know we overcounted the number of values we removed 
761                     // when we subtracted count from m_numValuesInVector above.
762                     if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
763                         storage->m_numValuesInVector++;
764                     storage->m_vector[count + i].setWithoutWriteBarrier(source);
765                 }
766             } else {
767                 memmove(storage->m_vector + count,
768                     storage->m_vector,
769                     sizeof(JSValue) * startIndex);
770             }
771         }
772         // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
773         // the start of the Butterfly, which needs to point at the first indexed property in the used
774         // portion of the vector.
775         m_butterfly.setWithoutWriteBarrier(m_butterfly->shift(structure(), count));
776         storage = m_butterfly->arrayStorage();
777         storage->m_indexBias += count;
778
779         // Since we're consuming part of the vector by moving its beginning to the left,
780         // we need to modify the vector length appropriately.
781         storage->setVectorLength(vectorLength - count);
782     } else {
783         // The number of elements before the shift region is greater than or equal to the number 
784         // of elements after the shift region, so we move the elements after the shift region to the left.
785         if (storage->hasHoles()) {
786             for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
787                 unsigned destinationIndex = startIndex + i;
788                 JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
789                 JSValue dest = storage->m_vector[destinationIndex].get();
790                 // Any time we overwrite a hole we know we overcounted the number of values we removed 
791                 // when we subtracted count from m_numValuesInVector above.
792                 if (!dest && destinationIndex < firstIndexAfterShiftRegion)
793                     storage->m_numValuesInVector++;
794                 storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
795             }
796         } else {
797             memmove(storage->m_vector + startIndex,
798                 storage->m_vector + firstIndexAfterShiftRegion,
799                 sizeof(JSValue) * numElementsAfterShiftRegion);
800         }
801         // Clear the slots of the elements we just moved.
802         unsigned startOfEmptyVectorTail = usedVectorLength - count;
803         for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
804             storage->m_vector[i].clear();
805         // We don't modify the index bias or the Butterfly pointer in this case because we're not changing 
806         // the start of the Butterfly, which needs to point at the first indexed property in the used 
807         // portion of the vector. We also don't modify the vector length because we're not actually changing
808         // its length; we're just using less of it.
809     }
810     
811     return true;
812 }
813
814 bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
815 {
816     VM& vm = exec->vm();
817     RELEASE_ASSERT(count > 0);
818     
819     switch (indexingType()) {
820     case ArrayClass:
821         return true;
822         
823     case ArrayWithUndecided:
824         // Don't handle this because it's confusing and it shouldn't come up.
825         return false;
826         
827     case ArrayWithInt32:
828     case ArrayWithContiguous: {
829         unsigned oldLength = m_butterfly->publicLength();
830         RELEASE_ASSERT(count <= oldLength);
831         
832         // We may have to walk the entire array to do the shift. We're willing to do
833         // so only if it's not horribly slow.
834         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
835             return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
836
837         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
838         // is totally not fine, since we might have to read from the proto chain.
839         // We have to check for holes before we start moving things around so that we don't get halfway 
840         // through shifting and then realize we should have been in ArrayStorage mode.
841         unsigned end = oldLength - count;
842         if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
843             for (unsigned i = startIndex; i < end; ++i) {
844                 JSValue v = m_butterfly->contiguous()[i + count].get();
845                 if (UNLIKELY(!v)) {
846                     startIndex = i;
847                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
848                 }
849                 m_butterfly->contiguous()[i].setWithoutWriteBarrier(v);
850             }
851         } else {
852             memmove(m_butterfly->contiguous().data() + startIndex, 
853                 m_butterfly->contiguous().data() + startIndex + count, 
854                 sizeof(JSValue) * (end - startIndex));
855         }
856
857         for (unsigned i = end; i < oldLength; ++i)
858             m_butterfly->contiguous()[i].clear();
859         
860         m_butterfly->setPublicLength(oldLength - count);
861         return true;
862     }
863         
864     case ArrayWithDouble: {
865         unsigned oldLength = m_butterfly->publicLength();
866         RELEASE_ASSERT(count <= oldLength);
867         
868         // We may have to walk the entire array to do the shift. We're willing to do
869         // so only if it's not horribly slow.
870         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
871             return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
872
873         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
874         // is totally not fine, since we might have to read from the proto chain.
875         // We have to check for holes before we start moving things around so that we don't get halfway 
876         // through shifting and then realize we should have been in ArrayStorage mode.
877         unsigned end = oldLength - count;
878         if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
879             for (unsigned i = startIndex; i < end; ++i) {
880                 double v = m_butterfly->contiguousDouble()[i + count];
881                 if (UNLIKELY(v != v)) {
882                     startIndex = i;
883                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
884                 }
885                 m_butterfly->contiguousDouble()[i] = v;
886             }
887         } else {
888             memmove(m_butterfly->contiguousDouble().data() + startIndex,
889                 m_butterfly->contiguousDouble().data() + startIndex + count,
890                 sizeof(JSValue) * (end - startIndex));
891         }
892         for (unsigned i = end; i < oldLength; ++i)
893             m_butterfly->contiguousDouble()[i] = PNaN;
894         
895         m_butterfly->setPublicLength(oldLength - count);
896         return true;
897     }
898         
899     case ArrayWithArrayStorage:
900     case ArrayWithSlowPutArrayStorage:
901         return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
902         
903     default:
904         CRASH();
905         return false;
906     }
907 }
908
909 // Returns true if the unshift can be handled, false to fallback.    
910 bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
911 {
912     unsigned length = storage->length();
913
914     RELEASE_ASSERT(startIndex <= length);
915
916     // If the array contains holes or is otherwise in an abnormal state,
917     // use the generic algorithm in ArrayPrototype.
918     if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
919         return false;
920
921     bool moveFront = !startIndex || startIndex < length / 2;
922
923     unsigned vectorLength = storage->vectorLength();
924
925     if (moveFront && storage->m_indexBias >= count) {
926         Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
927         storage = newButterfly->arrayStorage();
928         storage->m_indexBias -= count;
929         storage->setVectorLength(vectorLength + count);
930         setButterflyWithoutChangingStructure(exec->vm(), newButterfly);
931     } else if (!moveFront && vectorLength - length >= count)
932         storage = storage->butterfly()->arrayStorage();
933     else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
934         storage = arrayStorage();
935     else {
936         throwOutOfMemoryError(exec);
937         return true;
938     }
939
940     WriteBarrier<Unknown>* vector = storage->m_vector;
941
942     if (startIndex) {
943         if (moveFront)
944             memmove(vector, vector + count, startIndex * sizeof(JSValue));
945         else if (length - startIndex)
946             memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
947     }
948
949     for (unsigned i = 0; i < count; i++)
950         vector[i + startIndex].clear();
951     return true;
952 }
953
954 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
955 {
956     switch (indexingType()) {
957     case ArrayClass:
958     case ArrayWithUndecided:
959         // We could handle this. But it shouldn't ever come up, so we won't.
960         return false;
961
962     case ArrayWithInt32:
963     case ArrayWithContiguous: {
964         unsigned oldLength = m_butterfly->publicLength();
965         
966         // We may have to walk the entire array to do the unshift. We're willing to do so
967         // only if it's not horribly slow.
968         if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
969             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
970         
971         ensureLength(exec->vm(), oldLength + count);
972
973         // We have to check for holes before we start moving things around so that we don't get halfway 
974         // through shifting and then realize we should have been in ArrayStorage mode.
975         for (unsigned i = oldLength; i-- > startIndex;) {
976             JSValue v = m_butterfly->contiguous()[i].get();
977             if (UNLIKELY(!v))
978                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
979         }
980
981         for (unsigned i = oldLength; i-- > startIndex;) {
982             JSValue v = m_butterfly->contiguous()[i].get();
983             ASSERT(v);
984             m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
985         }
986         
987         // NOTE: we're leaving being garbage in the part of the array that we shifted out
988         // of. This is fine because the caller is required to store over that area, and
989         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
990         // as storing over an existing element.
991         
992         return true;
993     }
994         
995     case ArrayWithDouble: {
996         unsigned oldLength = m_butterfly->publicLength();
997         
998         // We may have to walk the entire array to do the unshift. We're willing to do so
999         // only if it's not horribly slow.
1000         if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
1001             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1002         
1003         ensureLength(exec->vm(), oldLength + count);
1004         
1005         // We have to check for holes before we start moving things around so that we don't get halfway 
1006         // through shifting and then realize we should have been in ArrayStorage mode.
1007         for (unsigned i = oldLength; i-- > startIndex;) {
1008             double v = m_butterfly->contiguousDouble()[i];
1009             if (UNLIKELY(v != v))
1010                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1011         }
1012
1013         for (unsigned i = oldLength; i-- > startIndex;) {
1014             double v = m_butterfly->contiguousDouble()[i];
1015             ASSERT(v == v);
1016             m_butterfly->contiguousDouble()[i + count] = v;
1017         }
1018         
1019         // NOTE: we're leaving being garbage in the part of the array that we shifted out
1020         // of. This is fine because the caller is required to store over that area, and
1021         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1022         // as storing over an existing element.
1023         
1024         return true;
1025     }
1026         
1027     case ArrayWithArrayStorage:
1028     case ArrayWithSlowPutArrayStorage:
1029         return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
1030         
1031     default:
1032         CRASH();
1033         return false;
1034     }
1035 }
1036
1037 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
1038 {
1039     unsigned i = 0;
1040     unsigned vectorEnd;
1041     WriteBarrier<Unknown>* vector;
1042     
1043     switch (indexingType()) {
1044     case ArrayClass:
1045         return;
1046         
1047     case ArrayWithUndecided: {
1048         vector = 0;
1049         vectorEnd = 0;
1050         break;
1051     }
1052         
1053     case ArrayWithInt32:
1054     case ArrayWithContiguous: {
1055         vectorEnd = m_butterfly->publicLength();
1056         vector = m_butterfly->contiguous().data();
1057         break;
1058     }
1059         
1060     case ArrayWithDouble: {
1061         vector = 0;
1062         vectorEnd = 0;
1063         for (; i < m_butterfly->publicLength(); ++i) {
1064             double v = butterfly()->contiguousDouble()[i];
1065             if (v != v)
1066                 break;
1067             args.append(JSValue(JSValue::EncodeAsDouble, v));
1068         }
1069         break;
1070     }
1071     
1072     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1073         ArrayStorage* storage = m_butterfly->arrayStorage();
1074         
1075         vector = storage->m_vector;
1076         vectorEnd = min(storage->length(), storage->vectorLength());
1077         break;
1078     }
1079         
1080     default:
1081         CRASH();
1082 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1083         vector = 0;
1084         vectorEnd = 0;
1085         break;
1086 #endif
1087     }
1088     
1089     for (; i < vectorEnd; ++i) {
1090         WriteBarrier<Unknown>& v = vector[i];
1091         if (!v)
1092             break;
1093         args.append(v.get());
1094     }
1095
1096     // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1097     for (; i < length(); ++i)
1098         args.append(get(exec, i));
1099 }
1100
1101 void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
1102 {
1103     unsigned i = offset;
1104     WriteBarrier<Unknown>* vector;
1105     unsigned vectorEnd;
1106     length += offset; // We like to think of the length as being our length, rather than the output length.
1107
1108     // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
1109     ASSERT(length == this->length());
1110
1111     switch (indexingType()) {
1112     case ArrayClass:
1113         return;
1114         
1115     case ArrayWithUndecided: {
1116         vector = 0;
1117         vectorEnd = 0;
1118         break;
1119     }
1120         
1121     case ArrayWithInt32:
1122     case ArrayWithContiguous: {
1123         vector = m_butterfly->contiguous().data();
1124         vectorEnd = m_butterfly->publicLength();
1125         break;
1126     }
1127         
1128     case ArrayWithDouble: {
1129         vector = 0;
1130         vectorEnd = 0;
1131         for (; i < m_butterfly->publicLength(); ++i) {
1132             ASSERT(i < butterfly()->vectorLength());
1133             double v = m_butterfly->contiguousDouble()[i];
1134             if (v != v)
1135                 break;
1136             exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
1137         }
1138         break;
1139     }
1140         
1141     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1142         ArrayStorage* storage = m_butterfly->arrayStorage();
1143         vector = storage->m_vector;
1144         vectorEnd = min(length, storage->vectorLength());
1145         break;
1146     }
1147         
1148     default:
1149         CRASH();
1150 #if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
1151         vector = 0;
1152         vectorEnd = 0;
1153         break;
1154 #endif
1155     }
1156     
1157     for (; i < vectorEnd; ++i) {
1158         WriteBarrier<Unknown>& v = vector[i];
1159         if (!v)
1160             break;
1161         exec->r(firstElementDest + i - offset) = v.get();
1162     }
1163     
1164     for (; i < length; ++i) {
1165         exec->r(firstElementDest + i - offset) = get(exec, i);
1166         if (UNLIKELY(exec->vm().exception()))
1167             return;
1168     }
1169 }
1170
1171 } // namespace JSC