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