Clients of JSArray::tryCreateForInitializationPrivate() should do their own null...
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSArray.cpp
index 9007b5f..ff9e054 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2003, 2007, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003-2017 Apple Inc. All rights reserved.
  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
  *
 
 #include "ArrayPrototype.h"
 #include "ButterflyInlines.h"
-#include "CachedCall.h"
-#include "CopiedSpace.h"
-#include "CopiedSpaceInlines.h"
+#include "CodeBlock.h"
 #include "Error.h"
-#include "Executable.h"
 #include "GetterSetter.h"
 #include "IndexingHeaderInlines.h"
+#include "JSArrayInlines.h"
+#include "JSCInlines.h"
 #include "PropertyNameArray.h"
-#include "Reject.h"
-#include <wtf/AVLTree.h>
+#include "TypeError.h"
 #include <wtf/Assertions.h>
-#include <wtf/OwnPtr.h>
-#include <Operations.h>
 
 using namespace std;
 using namespace WTF;
 
 namespace JSC {
 
+static const char* const LengthExceededTheMaximumArrayLengthError = "Length exceeded the maximum array length";
+
 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
 
-const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
+const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(JSArray)};
 
 Butterfly* createArrayButterflyInDictionaryIndexingMode(
     VM& vm, JSCell* intendedOwner, unsigned initialLength)
@@ -62,6 +60,54 @@ Butterfly* createArrayButterflyInDictionaryIndexingMode(
     return butterfly;
 }
 
+JSArray* JSArray::tryCreateForInitializationPrivate(VM& vm, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
+{
+    if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH))
+        return 0;
+
+    unsigned outOfLineStorage = structure->outOfLineCapacity();
+
+    Butterfly* butterfly;
+    IndexingType indexingType = structure->indexingType();
+    if (LIKELY(!hasAnyArrayStorage(indexingType))) {
+        ASSERT(
+            hasUndecided(indexingType)
+            || hasInt32(indexingType)
+            || hasDouble(indexingType)
+            || hasContiguous(indexingType));
+
+        unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength);
+        void* temp = vm.auxiliarySpace.tryAllocate(deferralContext, Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)));
+        if (UNLIKELY(!temp))
+            return nullptr;
+        butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
+        butterfly->setVectorLength(vectorLength);
+        butterfly->setPublicLength(initialLength);
+        if (hasDouble(indexingType)) {
+            for (unsigned i = initialLength; i < vectorLength; ++i)
+                butterfly->contiguousDouble()[i] = PNaN;
+        } else {
+            for (unsigned i = initialLength; i < vectorLength; ++i)
+                butterfly->contiguous()[i].clear();
+        }
+    } else {
+        unsigned vectorLength = ArrayStorage::optimalVectorLength(0, structure, initialLength);
+        void* temp = vm.auxiliarySpace.tryAllocate(deferralContext, Butterfly::totalSize(0, outOfLineStorage, true, ArrayStorage::sizeFor(vectorLength)));
+        if (UNLIKELY(!temp))
+            return nullptr;
+        butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
+        *butterfly->indexingHeader() = indexingHeaderForArrayStorage(initialLength, vectorLength);
+        ArrayStorage* storage = butterfly->arrayStorage();
+        storage->m_indexBias = 0;
+        storage->m_sparseMap.clear();
+        storage->m_numValuesInVector = initialLength;
+        for (unsigned i = initialLength; i < vectorLength; ++i)
+            storage->m_vector[i].clear();
+    }
+
+    return createWithButterfly(vm, deferralContext, structure, butterfly);
+}
+
 void JSArray::setLengthWritable(ExecState* exec, bool writable)
 {
     ASSERT(isLengthWritable() || !writable);
@@ -78,25 +124,28 @@ void JSArray::setLengthWritable(ExecState* exec, bool writable)
 // Defined in ES5.1 15.4.5.1
 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSArray* array = jsCast<JSArray*>(object);
 
     // 3. If P is "length", then
-    if (propertyName == exec->propertyNames().length) {
+    if (propertyName == vm.propertyNames->length) {
         // All paths through length definition call the default [[DefineOwnProperty]], hence:
         // from ES5.1 8.12.9 7.a.
         if (descriptor.configurablePresent() && descriptor.configurable())
-            return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
+            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError));
         // from ES5.1 8.12.9 7.b.
         if (descriptor.enumerablePresent() && descriptor.enumerable())
-            return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
+            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError));
 
         // a. If the [[Value]] field of Desc is absent, then
         // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
         if (descriptor.isAccessorDescriptor())
-            return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
+            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
         // from ES5.1 8.12.9 10.a.
         if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
-            return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
+            return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError));
         // This descriptor is either just making length read-only, or changing nothing!
         if (!descriptor.value()) {
             if (descriptor.writablePresent())
@@ -109,11 +158,12 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
         unsigned newLen = descriptor.value().toUInt32(exec);
         // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
         if (newLen != descriptor.value().toNumber(exec)) {
-            exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+            JSC::throwException(exec, scope, createRangeError(exec, ASCIILiteral("Invalid array length")));
             return false;
         }
 
         // Based on SameValue check in 8.12.9, this is always okay.
+        // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
         if (newLen == array->length()) {
             if (descriptor.writablePresent())
                 array->setLengthWritable(exec, descriptor.writable());
@@ -125,7 +175,7 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
         // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
         // g. Reject if oldLenDesc.[[Writable]] is false.
         if (!array->isLengthWritable())
-            return reject(exec, throwException, "Attempting to change value of a readonly property.");
+            return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyChangeError));
         
         // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
         // i. Else,
@@ -138,7 +188,9 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
         // l.i. Set oldLen to oldLen – 1.
         // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
         // l.iii. If deleteSucceeded is false, then
-        if (!array->setLength(exec, newLen, throwException)) {
+        bool success = array->setLength(exec, newLen, throwException);
+        ASSERT(!scope.exception() || !success);
+        if (!success) {
             // 1. Set newLenDesc.[[Value] to oldLen+1.
             // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
             // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
@@ -160,20 +212,23 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
 
     // 4. Else if P is an array index (15.4), then
     // a. Let index be ToUint32(P).
-    unsigned index = propertyName.asIndex();
-    if (index != PropertyName::NotAnIndex) {
+    if (std::optional<uint32_t> optionalIndex = parseIndex(propertyName)) {
         // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
+        uint32_t index = optionalIndex.value();
+        // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
         if (index >= array->length() && !array->isLengthWritable())
-            return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property.");
+            return typeError(exec, scope, throwException, ASCIILiteral("Attempting to define numeric property on array with non-writable length property."));
         // 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.
         // d. Reject if succeeded is false.
         // e. If index >= oldLen
         // e.i. Set oldLenDesc.[[Value]] to index + 1.
         // 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.
         // f. Return true.
+        scope.release();
         return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
     }
 
+    scope.release();
     return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
 }
 
@@ -190,21 +245,31 @@ bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName
 }
 
 // ECMA 15.4.5.1
-void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+bool JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSArray* thisObject = jsCast<JSArray*>(cell);
 
+    if (UNLIKELY(isThisValueAltered(slot, thisObject))) {
+        scope.release();
+        return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
+    }
+
     if (propertyName == exec->propertyNames().length) {
         unsigned newLength = value.toUInt32(exec);
+        RETURN_IF_EXCEPTION(scope, false);
         if (value.toNumber(exec) != static_cast<double>(newLength)) {
-            exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
-            return;
+            throwException(exec, scope, createRangeError(exec, ASCIILiteral("Invalid array length")));
+            return false;
         }
-        thisObject->setLength(exec, newLength, slot.isStrictMode());
-        return;
+        scope.release();
+        return thisObject->setLength(exec, newLength, slot.isStrictMode());
     }
 
-    JSObject::put(thisObject, exec, propertyName, value, slot);
+    scope.release();
+    return JSObject::put(thisObject, exec, propertyName, value, slot);
 }
 
 bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
@@ -228,20 +293,21 @@ void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, Pro
 {
     JSArray* thisObject = jsCast<JSArray*>(object);
 
-    if (mode == IncludeDontEnumProperties)
+    if (mode.includeDontEnumProperties())
         propertyNames.add(exec->propertyNames().length);
 
     JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
 }
 
 // This method makes room in the vector, but leaves the new space for count slots uncleared.
-bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
+bool JSArray::unshiftCountSlowCase(const AbstractLocker&, VM& vm, DeferGC&, bool addToFront, unsigned count)
 {
     ArrayStorage* storage = ensureArrayStorage(vm);
     Butterfly* butterfly = storage->butterfly();
-    unsigned propertyCapacity = structure()->outOfLineCapacity();
-    unsigned propertySize = structure()->outOfLineSize();
-
+    Structure* structure = this->structure(vm);
+    unsigned propertyCapacity = structure->outOfLineCapacity();
+    unsigned propertySize = structure->outOfLineSize();
+    
     // If not, we should have handled this on the fast path.
     ASSERT(!addToFront || count > storage->m_indexBias);
 
@@ -253,7 +319,8 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
     //  * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
 
     unsigned length = storage->length();
-    unsigned usedVectorLength = min(storage->vectorLength(), length);
+    unsigned oldVectorLength = storage->vectorLength();
+    unsigned usedVectorLength = min(oldVectorLength, length);
     ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
     // Check that required vector length is possible, in an overflow-safe fashion.
     if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
@@ -264,23 +331,29 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
     ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
     unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
     // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
-    unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1);
+    // FIXME: This code should be fixed to avoid internal fragmentation. It's not super high
+    // priority since increaseVectorLength() will "fix" any mistakes we make, but it would be cool
+    // to get this right eventually.
+    unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_ARRAY_STORAGE_VECTOR_LEN, requiredVectorLength) << 1);
 
     // Step 2:
     // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
 
-    DeferGC deferGC(vm.heap);
     void* newAllocBase = 0;
     unsigned newStorageCapacity;
+    bool allocatedNewStorage;
     // If the current storage array is sufficiently large (but not too large!) then just keep using it.
     if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
-        newAllocBase = butterfly->base(structure());
+        newAllocBase = butterfly->base(structure);
         newStorageCapacity = currentCapacity;
+        allocatedNewStorage = false;
     } else {
         size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
-        if (!vm.heap.tryAllocateStorage(this, newSize, &newAllocBase))
+        newAllocBase = vm.auxiliarySpace.tryAllocate(newSize);
+        if (!newAllocBase)
             return false;
         newStorageCapacity = desiredCapacity;
+        allocatedNewStorage = true;
     }
 
     // Step 3:
@@ -298,7 +371,7 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
         // Atomic decay, + the post-capacity cannot be greater than what is available.
         postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
         // If we're moving contents within the same allocation, the post-capacity is being reduced.
-        ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length);
+        ASSERT(newAllocBase != butterfly->base(structure) || postCapacity < storage->vectorLength() - length);
     }
 
     unsigned newVectorLength = requiredVectorLength + postCapacity;
@@ -310,33 +383,43 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
         ASSERT(count + usedVectorLength <= newVectorLength);
         memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
         memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
-    } else if ((newAllocBase != butterfly->base(structure())) || (newIndexBias != storage->m_indexBias)) {
+        
+        if (allocatedNewStorage) {
+            // We will set the vectorLength to newVectorLength. We populated requiredVectorLength
+            // (usedVectorLength + count), which is less. Clear the difference.
+            for (unsigned i = requiredVectorLength; i < newVectorLength; ++i)
+                newButterfly->arrayStorage()->m_vector[i].clear();
+        }
+    } else if ((newAllocBase != butterfly->base(structure)) || (newIndexBias != storage->m_indexBias)) {
         memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
         memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
-
-        WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector;
+        
         for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
-            newVector[i].clear();
+            newButterfly->arrayStorage()->m_vector[i].clear();
     }
 
     newButterfly->arrayStorage()->setVectorLength(newVectorLength);
     newButterfly->arrayStorage()->m_indexBias = newIndexBias;
-    setButterflyWithoutChangingStructure(vm, newButterfly);
+    
+    setButterfly(vm, newButterfly);
 
     return true;
 }
 
 bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
 {
-    unsigned length = storage->length();
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
+    unsigned length = storage->length();
+    
     // If the length is read only then we enter sparse mode, so should enter the following 'if'.
     ASSERT(isLengthWritable() || storage->m_sparseMap);
 
     if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
         // Fail if the length is not writable.
         if (map->lengthIsReadOnly())
-            return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
+            return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyWriteError));
 
         if (newLength < length) {
             // Copy any keys we might be interested in into a vector.
@@ -361,7 +444,7 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
                     ASSERT(it != map->notFound());
                     if (it->value.attributes & DontDelete) {
                         storage->setLength(index + 1);
-                        return reject(exec, throwException, "Unable to delete property.");
+                        return typeError(exec, scope, throwException, ASCIILiteral(UnableToDeletePropertyError));
                     }
                     map->remove(it);
                 }
@@ -379,7 +462,7 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
         unsigned usedVectorLength = min(length, storage->vectorLength());
         for (unsigned i = newLength; i < usedVectorLength; ++i) {
             WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
-            bool hadValue = valueSlot;
+            bool hadValue = !!valueSlot;
             valueSlot.clear();
             storage->m_numValuesInVector -= hadValue;
         }
@@ -390,49 +473,118 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
     return true;
 }
 
+bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    if (!canFastCopy(vm, otherArray))
+        return false;
+
+    IndexingType type = indexingType();
+    IndexingType copyType = mergeIndexingTypeForCopying(otherArray->indexingType());
+    if (type == ArrayWithUndecided && copyType != NonArray) {
+        if (copyType == ArrayWithInt32)
+            convertUndecidedToInt32(vm);
+        else if (copyType == ArrayWithDouble)
+            convertUndecidedToDouble(vm);
+        else if (copyType == ArrayWithContiguous)
+            convertUndecidedToContiguous(vm);
+        else {
+            ASSERT(copyType == ArrayWithUndecided);
+            return true;
+        }
+    } else if (type != copyType)
+        return false;
+
+    unsigned otherLength = otherArray->length();
+    Checked<unsigned, RecordOverflow> checkedNewLength = startIndex;
+    checkedNewLength += otherLength;
+
+    unsigned newLength;
+    if (checkedNewLength.safeGet(newLength) == CheckedState::DidOverflow) {
+        throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
+        return false;
+    }
+
+    if (newLength >= MIN_SPARSE_ARRAY_INDEX)
+        return false;
+
+    if (!ensureLength(vm, newLength)) {
+        throwOutOfMemoryError(exec, scope);
+        return false;
+    }
+    ASSERT(copyType == indexingType());
+
+    if (type == ArrayWithDouble)
+        memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
+    else
+        memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
+
+    return true;
+}
+
 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
 {
-    switch (structure()->indexingType()) {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Butterfly* butterfly = m_butterfly.get();
+    switch (indexingType()) {
     case ArrayClass:
         if (!newLength)
             return true;
         if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
+            scope.release();
             return setLengthWithArrayStorage(
                 exec, newLength, throwException,
-                convertContiguousToArrayStorage(exec->vm()));
+                ensureArrayStorage(vm));
         }
-        createInitialUndecided(exec->vm(), newLength);
+        createInitialUndecided(vm, newLength);
         return true;
         
     case ArrayWithUndecided:
     case ArrayWithInt32:
     case ArrayWithDouble:
-    case ArrayWithContiguous:
-        if (newLength == m_butterfly->publicLength())
+    case ArrayWithContiguous: {
+        if (newLength == butterfly->publicLength())
             return true;
         if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push.
             || (newLength >= MIN_SPARSE_ARRAY_INDEX
                 && !isDenseEnoughForVector(newLength, countElements()))) {
+            scope.release();
             return setLengthWithArrayStorage(
                 exec, newLength, throwException,
-                ensureArrayStorage(exec->vm()));
+                ensureArrayStorage(vm));
         }
-        if (newLength > m_butterfly->publicLength()) {
-            ensureLength(exec->vm(), newLength);
+        if (newLength > butterfly->publicLength()) {
+            if (!ensureLength(vm, newLength)) {
+                throwOutOfMemoryError(exec, scope);
+                return false;
+            }
+            return true;
+        }
+
+        unsigned lengthToClear = butterfly->publicLength() - newLength;
+        unsigned costToAllocateNewButterfly = 64; // a heuristic.
+        if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
+            reallocateAndShrinkButterfly(vm, newLength);
             return true;
         }
-        if (structure()->indexingType() == ArrayWithDouble) {
-            for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
-                m_butterfly->contiguousDouble()[i] = QNaN;
+
+        if (indexingType() == ArrayWithDouble) {
+            for (unsigned i = butterfly->publicLength(); i-- > newLength;)
+                butterfly->contiguousDouble()[i] = PNaN;
         } else {
-            for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
-                m_butterfly->contiguous()[i].clear();
+            for (unsigned i = butterfly->publicLength(); i-- > newLength;)
+                butterfly->contiguous()[i].clear();
         }
-        m_butterfly->setPublicLength(newLength);
+        butterfly->setPublicLength(newLength);
         return true;
+    }
         
     case ArrayWithArrayStorage:
     case ArrayWithSlowPutArrayStorage:
+        scope.release();
         return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
         
     default:
@@ -443,56 +595,61 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
 
 JSValue JSArray::pop(ExecState* exec)
 {
-    switch (structure()->indexingType()) {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Butterfly* butterfly = m_butterfly.get();
+    
+    switch (indexingType()) {
     case ArrayClass:
         return jsUndefined();
         
     case ArrayWithUndecided:
-        if (!m_butterfly->publicLength())
+        if (!butterfly->publicLength())
             return jsUndefined();
         // We have nothing but holes. So, drop down to the slow version.
         break;
         
     case ArrayWithInt32:
     case ArrayWithContiguous: {
-        unsigned length = m_butterfly->publicLength();
+        unsigned length = butterfly->publicLength();
         
         if (!length--)
             return jsUndefined();
         
-        RELEASE_ASSERT(length < m_butterfly->vectorLength());
-        JSValue value = m_butterfly->contiguous()[length].get();
+        RELEASE_ASSERT(length < butterfly->vectorLength());
+        JSValue value = butterfly->contiguous()[length].get();
         if (value) {
-            m_butterfly->contiguous()[length].clear();
-            m_butterfly->setPublicLength(length);
+            butterfly->contiguous()[length].clear();
+            butterfly->setPublicLength(length);
             return value;
         }
         break;
     }
         
     case ArrayWithDouble: {
-        unsigned length = m_butterfly->publicLength();
+        unsigned length = butterfly->publicLength();
         
         if (!length--)
             return jsUndefined();
         
-        RELEASE_ASSERT(length < m_butterfly->vectorLength());
-        double value = m_butterfly->contiguousDouble()[length];
+        RELEASE_ASSERT(length < butterfly->vectorLength());
+        double value = butterfly->contiguousDouble()[length];
         if (value == value) {
-            m_butterfly->contiguousDouble()[length] = QNaN;
-            m_butterfly->setPublicLength(length);
+            butterfly->contiguousDouble()[length] = PNaN;
+            butterfly->setPublicLength(length);
             return JSValue(JSValue::EncodeAsDouble, value);
         }
         break;
     }
         
     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
-        ArrayStorage* storage = m_butterfly->arrayStorage();
+        ArrayStorage* storage = butterfly->arrayStorage();
     
         unsigned length = storage->length();
         if (!length) {
             if (!isLengthWritable())
-                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+                throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
             return jsUndefined();
         }
 
@@ -520,14 +677,16 @@ JSValue JSArray::pop(ExecState* exec)
     unsigned index = getArrayLength() - 1;
     // Let element be the result of calling the [[Get]] internal method of O with argument indx.
     JSValue element = get(exec, index);
-    if (exec->hadException())
-        return jsUndefined();
+    RETURN_IF_EXCEPTION(scope, JSValue());
     // Call the [[Delete]] internal method of O with arguments indx and true.
-    if (!deletePropertyByIndex(this, exec, index)) {
-        throwTypeError(exec, "Unable to delete property.");
+    bool success = deletePropertyByIndex(this, exec, index);
+    RETURN_IF_EXCEPTION(scope, JSValue());
+    if (!success) {
+        throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
         return jsUndefined();
     }
     // Call the [[Put]] internal method of O with arguments "length", indx, and true.
+    scope.release();
     setLength(exec, index, true);
     // Return element.
     return element;
@@ -538,130 +697,146 @@ JSValue JSArray::pop(ExecState* exec)
 //  - pushing to an array of length 2^32-1 stores the property, but throws a range error.
 void JSArray::push(ExecState* exec, JSValue value)
 {
-    switch (structure()->indexingType()) {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Butterfly* butterfly = m_butterfly.get();
+    
+    switch (indexingType()) {
     case ArrayClass: {
-        createInitialUndecided(exec->vm(), 0);
+        createInitialUndecided(vm, 0);
         FALLTHROUGH;
     }
         
     case ArrayWithUndecided: {
-        convertUndecidedForValue(exec->vm(), value);
+        convertUndecidedForValue(vm, value);
+        scope.release();
         push(exec, value);
         return;
     }
         
     case ArrayWithInt32: {
         if (!value.isInt32()) {
-            convertInt32ForValue(exec->vm(), value);
+            convertInt32ForValue(vm, value);
+            scope.release();
             push(exec, value);
             return;
         }
 
-        unsigned length = m_butterfly->publicLength();
-        ASSERT(length <= m_butterfly->vectorLength());
-        if (length < m_butterfly->vectorLength()) {
-            m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
-            m_butterfly->setPublicLength(length + 1);
+        unsigned length = butterfly->publicLength();
+        ASSERT(length <= butterfly->vectorLength());
+        if (length < butterfly->vectorLength()) {
+            butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
+            butterfly->setPublicLength(length + 1);
             return;
         }
         
-        if (length > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, length, value, true);
-            if (!exec->hadException())
-                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+        if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
+            methodTable(vm)->putByIndex(this, exec, length, value, true);
+            if (!scope.exception())
+                throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
             return;
         }
-        
+
+        scope.release();
         putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
         return;
     }
 
     case ArrayWithContiguous: {
-        unsigned length = m_butterfly->publicLength();
-        ASSERT(length <= m_butterfly->vectorLength());
-        if (length < m_butterfly->vectorLength()) {
-            m_butterfly->contiguous()[length].set(exec->vm(), this, value);
-            m_butterfly->setPublicLength(length + 1);
+        unsigned length = butterfly->publicLength();
+        ASSERT(length <= butterfly->vectorLength());
+        if (length < butterfly->vectorLength()) {
+            butterfly->contiguous()[length].set(vm, this, value);
+            butterfly->setPublicLength(length + 1);
             return;
         }
         
-        if (length > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, length, value, true);
-            if (!exec->hadException())
-                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+        if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
+            methodTable(vm)->putByIndex(this, exec, length, value, true);
+            if (!scope.exception())
+                throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
             return;
         }
-        
+
+        scope.release();
         putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
         return;
     }
         
     case ArrayWithDouble: {
         if (!value.isNumber()) {
-            convertDoubleToContiguous(exec->vm());
+            convertDoubleToContiguous(vm);
+            scope.release();
             push(exec, value);
             return;
         }
         double valueAsDouble = value.asNumber();
         if (valueAsDouble != valueAsDouble) {
-            convertDoubleToContiguous(exec->vm());
+            convertDoubleToContiguous(vm);
+            scope.release();
             push(exec, value);
             return;
         }
 
-        unsigned length = m_butterfly->publicLength();
-        ASSERT(length <= m_butterfly->vectorLength());
-        if (length < m_butterfly->vectorLength()) {
-            m_butterfly->contiguousDouble()[length] = valueAsDouble;
-            m_butterfly->setPublicLength(length + 1);
+        unsigned length = butterfly->publicLength();
+        ASSERT(length <= butterfly->vectorLength());
+        if (length < butterfly->vectorLength()) {
+            butterfly->contiguousDouble()[length] = valueAsDouble;
+            butterfly->setPublicLength(length + 1);
             return;
         }
         
-        if (length > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, length, value, true);
-            if (!exec->hadException())
-                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+        if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
+            methodTable(vm)->putByIndex(this, exec, length, value, true);
+            if (!scope.exception())
+                throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
             return;
         }
-        
+
+        scope.release();
         putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
-        break;
+        return;
     }
         
     case ArrayWithSlowPutArrayStorage: {
         unsigned oldLength = length();
-        if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) {
-            if (!exec->hadException() && oldLength < 0xFFFFFFFFu)
+        bool putResult = false;
+        if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult)) {
+            if (!scope.exception() && oldLength < 0xFFFFFFFFu) {
+                scope.release();
                 setLength(exec, oldLength + 1, true);
+            }
             return;
         }
         FALLTHROUGH;
     }
         
     case ArrayWithArrayStorage: {
-        ArrayStorage* storage = m_butterfly->arrayStorage();
+        ArrayStorage* storage = butterfly->arrayStorage();
 
         // Fast case - push within vector, always update m_length & m_numValuesInVector.
         unsigned length = storage->length();
         if (length < storage->vectorLength()) {
-            storage->m_vector[length].set(exec->vm(), this, value);
+            storage->m_vector[length].set(vm, this, value);
             storage->setLength(length + 1);
             ++storage->m_numValuesInVector;
             return;
         }
 
         // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
-        if (storage->length() > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, storage->length(), value, true);
+        if (UNLIKELY(storage->length() > MAX_ARRAY_INDEX)) {
+            methodTable(vm)->putByIndex(this, exec, storage->length(), value, true);
             // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
-            if (!exec->hadException())
-                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+            if (!scope.exception())
+                throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
             return;
         }
 
         // Handled the same as putIndex.
+        scope.release();
         putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
-        break;
+        return;
     }
         
     default:
@@ -669,15 +844,48 @@ void JSArray::push(ExecState* exec, JSValue value)
     }
 }
 
-bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage* storage)
+JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
+{
+    auto arrayType = indexingType();
+    switch (arrayType) {
+    case ArrayWithDouble:
+    case ArrayWithInt32:
+    case ArrayWithContiguous: {
+        VM& vm = exec.vm();
+        if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm))
+            return nullptr;
+
+        Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(arrayType);
+        JSArray* resultArray = JSArray::tryCreateForInitializationPrivate(vm, resultStructure, count);
+        if (UNLIKELY(!resultArray))
+            return nullptr;
+
+        auto& resultButterfly = *resultArray->butterfly();
+        if (arrayType == ArrayWithDouble)
+            memcpy(resultButterfly.contiguousDouble().data(), m_butterfly.get()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
+        else
+            memcpy(resultButterfly.contiguous().data(), m_butterfly.get()->contiguous().data() + startIndex, sizeof(JSValue) * count);
+        resultButterfly.setPublicLength(count);
+
+        return resultArray;
+    }
+    default:
+        return nullptr;
+    }
+}
+
+bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
 {
     unsigned oldLength = storage->length();
     RELEASE_ASSERT(count <= oldLength);
     
     // If the array contains holes or is otherwise in an abnormal state,
     // use the generic algorithm in ArrayPrototype.
-    if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode() || shouldUseSlowPut(structure()->indexingType()))
+    if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm)) 
+        || hasSparseMap() 
+        || shouldUseSlowPut(indexingType())) {
         return false;
+    }
 
     if (!oldLength)
         return true;
@@ -694,6 +902,9 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
     if (startIndex >= vectorLength)
         return true;
     
+    DisallowGC disallowGC;
+    auto locker = holdLock(*this);
+    
     if (startIndex + count > vectorLength)
         count = vectorLength - startIndex;
     
@@ -711,16 +922,29 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
         // after the shift region, so we move the elements before to the right.
         if (numElementsBeforeShiftRegion) {
             RELEASE_ASSERT(count + startIndex <= vectorLength);
-            memmove(
-                storage->m_vector + count,
-                storage->m_vector,
-                sizeof(JSValue) * startIndex);
+            if (storage->hasHoles()) {
+                for (unsigned i = startIndex; i-- > 0;) {
+                    unsigned destinationIndex = count + i;
+                    JSValue source = storage->m_vector[i].get();
+                    JSValue dest = storage->m_vector[destinationIndex].get();
+                    // Any time we overwrite a hole we know we overcounted the number of values we removed 
+                    // when we subtracted count from m_numValuesInVector above.
+                    if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
+                        storage->m_numValuesInVector++;
+                    storage->m_vector[count + i].setWithoutWriteBarrier(source);
+                }
+            } else {
+                memmove(storage->m_vector + count,
+                    storage->m_vector,
+                    sizeof(JSValue) * startIndex);
+            }
         }
         // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
         // the start of the Butterfly, which needs to point at the first indexed property in the used
         // portion of the vector.
-        m_butterfly.setWithoutWriteBarrier(m_butterfly->shift(structure(), count));
-        storage = m_butterfly->arrayStorage();
+        Butterfly* butterfly = m_butterfly.get()->shift(structure(), count);
+        setButterfly(vm, butterfly);
+        storage = butterfly->arrayStorage();
         storage->m_indexBias += count;
 
         // Since we're consuming part of the vector by moving its beginning to the left,
@@ -729,10 +953,22 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
     } else {
         // The number of elements before the shift region is greater than or equal to the number 
         // of elements after the shift region, so we move the elements after the shift region to the left.
-        memmove(
-            storage->m_vector + startIndex,
-            storage->m_vector + firstIndexAfterShiftRegion,
-            sizeof(JSValue) * numElementsAfterShiftRegion);
+        if (storage->hasHoles()) {
+            for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
+                unsigned destinationIndex = startIndex + i;
+                JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
+                JSValue dest = storage->m_vector[destinationIndex].get();
+                // Any time we overwrite a hole we know we overcounted the number of values we removed 
+                // when we subtracted count from m_numValuesInVector above.
+                if (!dest && destinationIndex < firstIndexAfterShiftRegion)
+                    storage->m_numValuesInVector++;
+                storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
+            }
+        } else {
+            memmove(storage->m_vector + startIndex,
+                storage->m_vector + firstIndexAfterShiftRegion,
+                sizeof(JSValue) * numElementsAfterShiftRegion);
+        }
         // Clear the slots of the elements we just moved.
         unsigned startOfEmptyVectorTail = usedVectorLength - count;
         for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
@@ -746,11 +982,14 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
     return true;
 }
 
-bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
+bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
 {
+    VM& vm = exec->vm();
     RELEASE_ASSERT(count > 0);
+
+    Butterfly* butterfly = m_butterfly.get();
     
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return true;
         
@@ -760,78 +999,84 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
         
     case ArrayWithInt32:
     case ArrayWithContiguous: {
-        unsigned oldLength = m_butterfly->publicLength();
+        unsigned oldLength = butterfly->publicLength();
         RELEASE_ASSERT(count <= oldLength);
         
         // We may have to walk the entire array to do the shift. We're willing to do
         // so only if it's not horribly slow.
         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
-            return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+            return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
 
         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
         // is totally not fine, since we might have to read from the proto chain.
         // We have to check for holes before we start moving things around so that we don't get halfway 
         // through shifting and then realize we should have been in ArrayStorage mode.
         unsigned end = oldLength - count;
-        for (unsigned i = startIndex; i < end; ++i) {
-            JSValue v = m_butterfly->contiguous()[i + count].get();
-            if (UNLIKELY(!v))
-                return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+        if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
+            for (unsigned i = startIndex; i < end; ++i) {
+                JSValue v = butterfly->contiguous()[i + count].get();
+                if (UNLIKELY(!v)) {
+                    startIndex = i;
+                    return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
+                }
+                butterfly->contiguous()[i].setWithoutWriteBarrier(v);
+            }
+        } else {
+            memmove(butterfly->contiguous().data() + startIndex, 
+                butterfly->contiguous().data() + startIndex + count, 
+                sizeof(JSValue) * (end - startIndex));
         }
 
-        for (unsigned i = startIndex; i < end; ++i) {
-            JSValue v = m_butterfly->contiguous()[i + count].get();
-            ASSERT(v);
-            // No need for a barrier since we're just moving data around in the same vector.
-            // This is in line with our standing assumption that we won't have a deletion
-            // barrier.
-            m_butterfly->contiguous()[i].setWithoutWriteBarrier(v);
-        }
         for (unsigned i = end; i < oldLength; ++i)
-            m_butterfly->contiguous()[i].clear();
+            butterfly->contiguous()[i].clear();
+        
+        butterfly->setPublicLength(oldLength - count);
+
+        // Our memmoving of values around in the array could have concealed some of them from
+        // the collector. Let's make sure that the collector scans this object again.
+        vm.heap.writeBarrier(this);
         
-        m_butterfly->setPublicLength(oldLength - count);
         return true;
     }
         
     case ArrayWithDouble: {
-        unsigned oldLength = m_butterfly->publicLength();
+        unsigned oldLength = butterfly->publicLength();
         RELEASE_ASSERT(count <= oldLength);
         
         // We may have to walk the entire array to do the shift. We're willing to do
         // so only if it's not horribly slow.
         if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
-            return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+            return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
 
         // Storing to a hole is fine since we're still having a good time. But reading from a hole 
         // is totally not fine, since we might have to read from the proto chain.
         // We have to check for holes before we start moving things around so that we don't get halfway 
         // through shifting and then realize we should have been in ArrayStorage mode.
         unsigned end = oldLength - count;
-        for (unsigned i = startIndex; i < end; ++i) {
-            double v = m_butterfly->contiguousDouble()[i + count];
-            if (UNLIKELY(v != v))
-                return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
-        }
-            
-        for (unsigned i = startIndex; i < end; ++i) {
-            double v = m_butterfly->contiguousDouble()[i + count];
-            ASSERT(v == v);
-            // No need for a barrier since we're just moving data around in the same vector.
-            // This is in line with our standing assumption that we won't have a deletion
-            // barrier.
-            m_butterfly->contiguousDouble()[i] = v;
+        if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
+            for (unsigned i = startIndex; i < end; ++i) {
+                double v = butterfly->contiguousDouble()[i + count];
+                if (UNLIKELY(v != v)) {
+                    startIndex = i;
+                    return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
+                }
+                butterfly->contiguousDouble()[i] = v;
+            }
+        } else {
+            memmove(butterfly->contiguousDouble().data() + startIndex,
+                butterfly->contiguousDouble().data() + startIndex + count,
+                sizeof(JSValue) * (end - startIndex));
         }
         for (unsigned i = end; i < oldLength; ++i)
-            m_butterfly->contiguousDouble()[i] = QNaN;
+            butterfly->contiguousDouble()[i] = PNaN;
         
-        m_butterfly->setPublicLength(oldLength - count);
+        butterfly->setPublicLength(oldLength - count);
         return true;
     }
         
     case ArrayWithArrayStorage:
     case ArrayWithSlowPutArrayStorage:
-        return shiftCountWithArrayStorage(startIndex, count, arrayStorage());
+        return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
         
     default:
         CRASH();
@@ -842,31 +1087,39 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
 // Returns true if the unshift can be handled, false to fallback.    
 bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     unsigned length = storage->length();
 
     RELEASE_ASSERT(startIndex <= length);
 
     // If the array contains holes or is otherwise in an abnormal state,
     // use the generic algorithm in ArrayPrototype.
-    if (length != storage->m_numValuesInVector || storage->inSparseMode() || shouldUseSlowPut(structure()->indexingType()))
+    if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
         return false;
 
     bool moveFront = !startIndex || startIndex < length / 2;
 
     unsigned vectorLength = storage->vectorLength();
 
+    // Need to have GC deferred around the unshiftCountSlowCase(), since that leaves the butterfly in
+    // a weird state: some parts of it will be left uninitialized, which we will fill in here.
+    DeferGC deferGC(vm.heap);
+    auto locker = holdLock(*this);
+    
     if (moveFront && storage->m_indexBias >= count) {
         Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
         storage = newButterfly->arrayStorage();
         storage->m_indexBias -= count;
         storage->setVectorLength(vectorLength + count);
-        setButterflyWithoutChangingStructure(exec->vm(), newButterfly);
+        setButterfly(vm, newButterfly);
     } else if (!moveFront && vectorLength - length >= count)
         storage = storage->butterfly()->arrayStorage();
-    else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
+    else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count))
         storage = arrayStorage();
     else {
-        throwOutOfMemoryError(exec);
+        throwOutOfMemoryError(exec, scope);
         return true;
     }
 
@@ -881,12 +1134,18 @@ bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex,
 
     for (unsigned i = 0; i < count; i++)
         vector[i + startIndex].clear();
+    
     return true;
 }
 
 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
 {
-    switch (structure()->indexingType()) {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    Butterfly* butterfly = m_butterfly.get();
+    
+    switch (indexingType()) {
     case ArrayClass:
     case ArrayWithUndecided:
         // We could handle this. But it shouldn't ever come up, so we won't.
@@ -894,29 +1153,41 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
 
     case ArrayWithInt32:
     case ArrayWithContiguous: {
-        unsigned oldLength = m_butterfly->publicLength();
+        unsigned oldLength = butterfly->publicLength();
         
         // We may have to walk the entire array to do the unshift. We're willing to do so
         // only if it's not horribly slow.
-        if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
-            return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+        if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
+            scope.release();
+            return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+        }
         
-        ensureLength(exec->vm(), oldLength + count);
+        if (!ensureLength(vm, oldLength + count)) {
+            throwOutOfMemoryError(exec, scope);
+            return false;
+        }
+        butterfly = m_butterfly.get();
 
         // We have to check for holes before we start moving things around so that we don't get halfway 
         // through shifting and then realize we should have been in ArrayStorage mode.
         for (unsigned i = oldLength; i-- > startIndex;) {
-            JSValue v = m_butterfly->contiguous()[i].get();
-            if (UNLIKELY(!v))
-                return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+            JSValue v = butterfly->contiguous()[i].get();
+            if (UNLIKELY(!v)) {
+                scope.release();
+                return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+            }
         }
 
         for (unsigned i = oldLength; i-- > startIndex;) {
-            JSValue v = m_butterfly->contiguous()[i].get();
+            JSValue v = butterfly->contiguous()[i].get();
             ASSERT(v);
-            m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
+            butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
         }
         
+        // Our memmoving of values around in the array could have concealed some of them from
+        // the collector. Let's make sure that the collector scans this object again.
+        vm.heap.writeBarrier(this);
+        
         // NOTE: we're leaving being garbage in the part of the array that we shifted out
         // of. This is fine because the caller is required to store over that area, and
         // in contiguous mode storing into a hole is guaranteed to behave exactly the same
@@ -926,27 +1197,35 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
     }
         
     case ArrayWithDouble: {
-        unsigned oldLength = m_butterfly->publicLength();
+        unsigned oldLength = butterfly->publicLength();
         
         // We may have to walk the entire array to do the unshift. We're willing to do so
         // only if it's not horribly slow.
-        if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
-            return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+        if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
+            scope.release();
+            return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+        }
         
-        ensureLength(exec->vm(), oldLength + count);
+        if (!ensureLength(vm, oldLength + count)) {
+            throwOutOfMemoryError(exec, scope);
+            return false;
+        }
+        butterfly = m_butterfly.get();
         
         // We have to check for holes before we start moving things around so that we don't get halfway 
         // through shifting and then realize we should have been in ArrayStorage mode.
         for (unsigned i = oldLength; i-- > startIndex;) {
-            double v = m_butterfly->contiguousDouble()[i];
-            if (UNLIKELY(v != v))
-                return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+            double v = butterfly->contiguousDouble()[i];
+            if (UNLIKELY(v != v)) {
+                scope.release();
+                return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+            }
         }
 
         for (unsigned i = oldLength; i-- > startIndex;) {
-            double v = m_butterfly->contiguousDouble()[i];
+            double v = butterfly->contiguousDouble()[i];
             ASSERT(v == v);
-            m_butterfly->contiguousDouble()[i + count] = v;
+            butterfly->contiguousDouble()[i + count] = v;
         }
         
         // NOTE: we're leaving being garbage in the part of the array that we shifted out
@@ -959,6 +1238,7 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         
     case ArrayWithArrayStorage:
     case ArrayWithSlowPutArrayStorage:
+        scope.release();
         return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
         
     default:
@@ -967,526 +1247,15 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
     }
 }
 
-static int compareNumbersForQSortWithInt32(const void* a, const void* b)
-{
-    int32_t ia = static_cast<const JSValue*>(a)->asInt32();
-    int32_t ib = static_cast<const JSValue*>(b)->asInt32();
-    return ia - ib;
-}
-
-static int compareNumbersForQSortWithDouble(const void* a, const void* b)
-{
-    double da = *static_cast<const double*>(a);
-    double db = *static_cast<const double*>(b);
-    return (da > db) - (da < db);
-}
-
-static int compareNumbersForQSort(const void* a, const void* b)
-{
-    double da = static_cast<const JSValue*>(a)->asNumber();
-    double db = static_cast<const JSValue*>(b)->asNumber();
-    return (da > db) - (da < db);
-}
-
-static int compareByStringPairForQSort(const void* a, const void* b)
-{
-    const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
-    const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
-    return codePointCompare(va->second, vb->second);
-}
-
-template<IndexingType indexingType>
-void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
-    ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage);
-    
-    unsigned lengthNotIncludingUndefined;
-    unsigned newRelevantLength;
-    compactForSorting<indexingType>(
-        lengthNotIncludingUndefined,
-        newRelevantLength);
-    
-    ContiguousJSValues data = indexingData<indexingType>();
-    
-    if (indexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
-        throwOutOfMemoryError(exec);
-        return;
-    }
-    
-    if (!lengthNotIncludingUndefined)
-        return;
-    
-    bool allValuesAreNumbers = true;
-    switch (indexingType) {
-    case ArrayWithInt32:
-    case ArrayWithDouble:
-        break;
-        
-    default:
-        for (size_t i = 0; i < newRelevantLength; ++i) {
-            if (!data[i].isNumber()) {
-                allValuesAreNumbers = false;
-                break;
-            }
-        }
-        break;
-    }
-    
-    if (!allValuesAreNumbers)
-        return sort(exec, compareFunction, callType, callData);
-    
-    // For numeric comparison, which is fast, qsort is faster than mergesort. We
-    // also don't require mergesort's stability, since there's no user visible
-    // side-effect from swapping the order of equal primitive values.
-    int (*compare)(const void*, const void*);
-    switch (indexingType) {
-    case ArrayWithInt32:
-        compare = compareNumbersForQSortWithInt32;
-        break;
-        
-    case ArrayWithDouble:
-        compare = compareNumbersForQSortWithDouble;
-        ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(double));
-        break;
-        
-    default:
-        compare = compareNumbersForQSort;
-        break;
-    }
-    ASSERT(data.length() >= newRelevantLength);
-    qsort(data.data(), newRelevantLength, sizeof(WriteBarrier<Unknown>), compare);
-    return;
-}
-
-void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
-    ASSERT(!inSparseIndexingMode());
-
-    switch (structure()->indexingType()) {
-    case ArrayClass:
-        return;
-        
-    case ArrayWithInt32:
-        sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
-        break;
-        
-    case ArrayWithDouble:
-        sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
-        break;
-        
-    case ArrayWithContiguous:
-        sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
-        return;
-
-    case ArrayWithArrayStorage:
-        sortNumericVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
-        return;
-        
-    default:
-        CRASH();
-        return;
-    }
-}
-
-template <IndexingType> struct ContiguousTypeAccessor {
-    typedef WriteBarrier<Unknown> Type;
-    static JSValue getAsValue(ContiguousData<Type> data, size_t i) { return data[i].get(); }
-    static void setWithValue(VM& vm, JSArray* thisValue, ContiguousData<Type> data, size_t i, JSValue value)
-    {
-        data[i].set(vm, thisValue, value);
-    }
-    static void replaceDataReference(ContiguousData<Type>* outData, ContiguousJSValues inData)
-    {
-        *outData = inData;
-    }
-};
-
-template <> struct ContiguousTypeAccessor<ArrayWithDouble> {
-    typedef double Type;
-    static JSValue getAsValue(ContiguousData<Type> data, size_t i) { ASSERT(data[i] == data[i]); return JSValue(JSValue::EncodeAsDouble, data[i]); }
-    static void setWithValue(VM&, JSArray*, ContiguousData<Type> data, size_t i, JSValue value)
-    {
-        data[i] = value.asNumber();
-    }
-    static NO_RETURN_DUE_TO_CRASH void replaceDataReference(ContiguousData<Type>*, ContiguousJSValues)
-    {
-        RELEASE_ASSERT_WITH_MESSAGE(0, "Inconsistent indexing types during compact array sort.");
-    }
-};
-
-
-template<IndexingType indexingType, typename StorageType>
-void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> data, unsigned relevantLength)
-{
-    if (!relevantLength)
-        return;
-    
-    VM& vm = exec->vm();
-
-    // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
-    // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
-    // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
-    // random or otherwise changing results, effectively making compare function inconsistent.
-        
-    Vector<ValueStringPair, 0, UnsafeVectorOverflow> values(relevantLength);
-    if (!values.begin()) {
-        throwOutOfMemoryError(exec);
-        return;
-    }
-        
-    Heap::heap(this)->pushTempSortVector(&values);
-        
-    bool isSortingPrimitiveValues = true;
-
-    for (size_t i = 0; i < relevantLength; i++) {
-        JSValue value = ContiguousTypeAccessor<indexingType>::getAsValue(data, i);
-        ASSERT(indexingType != ArrayWithInt32 || value.isInt32());
-        ASSERT(!value.isUndefined());
-        values[i].first = value;
-        if (indexingType != ArrayWithDouble && indexingType != ArrayWithInt32)
-            isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
-    }
-        
-    // FIXME: The following loop continues to call toString on subsequent values even after
-    // a toString call raises an exception.
-        
-    for (size_t i = 0; i < relevantLength; i++)
-        values[i].second = values[i].first.toWTFStringInline(exec);
-        
-    if (exec->hadException()) {
-        Heap::heap(this)->popTempSortVector(&values);
-        return;
-    }
-        
-    // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
-    // than O(N log N).
-        
-#if HAVE(MERGESORT)
-    if (isSortingPrimitiveValues)
-        qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
-    else
-        mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
-#else
-    // FIXME: The qsort library function is likely to not be a stable sort.
-    // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
-    qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
-#endif
-    
-    // If the toString function changed the length of the array or vector storage,
-    // increase the length to handle the orignal number of actual values.
-    switch (indexingType) {
-    case ArrayWithInt32:
-    case ArrayWithDouble:
-    case ArrayWithContiguous:
-        ensureLength(vm, relevantLength);
-        break;
-        
-    case ArrayWithArrayStorage:
-        if (arrayStorage()->vectorLength() < relevantLength) {
-            increaseVectorLength(exec->vm(), relevantLength);
-            ContiguousTypeAccessor<indexingType>::replaceDataReference(&data, arrayStorage()->vector());
-        }
-        if (arrayStorage()->length() < relevantLength)
-            arrayStorage()->setLength(relevantLength);
-        break;
-        
-    default:
-        CRASH();
-    }
-
-    for (size_t i = 0; i < relevantLength; i++)
-        ContiguousTypeAccessor<indexingType>::setWithValue(vm, this, data, i, values[i].first);
-    
-    Heap::heap(this)->popTempSortVector(&values);
-}
-
-void JSArray::sort(ExecState* exec)
-{
-    ASSERT(!inSparseIndexingMode());
-    
-    switch (structure()->indexingType()) {
-    case ArrayClass:
-    case ArrayWithUndecided:
-        return;
-        
-    case ArrayWithInt32: {
-        unsigned lengthNotIncludingUndefined;
-        unsigned newRelevantLength;
-        compactForSorting<ArrayWithInt32>(
-            lengthNotIncludingUndefined, newRelevantLength);
-        
-        sortCompactedVector<ArrayWithInt32>(
-            exec, m_butterfly->contiguousInt32(), lengthNotIncludingUndefined);
-        return;
-    }
-        
-    case ArrayWithDouble: {
-        unsigned lengthNotIncludingUndefined;
-        unsigned newRelevantLength;
-        compactForSorting<ArrayWithDouble>(
-            lengthNotIncludingUndefined, newRelevantLength);
-        
-        sortCompactedVector<ArrayWithDouble>(
-            exec, m_butterfly->contiguousDouble(), lengthNotIncludingUndefined);
-        return;
-    }
-        
-    case ArrayWithContiguous: {
-        unsigned lengthNotIncludingUndefined;
-        unsigned newRelevantLength;
-        compactForSorting<ArrayWithContiguous>(
-            lengthNotIncludingUndefined, newRelevantLength);
-        
-        sortCompactedVector<ArrayWithContiguous>(
-            exec, m_butterfly->contiguous(), lengthNotIncludingUndefined);
-        return;
-    }
-        
-    case ArrayWithArrayStorage: {
-        unsigned lengthNotIncludingUndefined;
-        unsigned newRelevantLength;
-        compactForSorting<ArrayWithArrayStorage>(
-            lengthNotIncludingUndefined, newRelevantLength);
-        ArrayStorage* storage = m_butterfly->arrayStorage();
-        ASSERT(!storage->m_sparseMap);
-        
-        sortCompactedVector<ArrayWithArrayStorage>(exec, storage->vector(), lengthNotIncludingUndefined);
-        return;
-    }
-        
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-    }
-}
-
-struct AVLTreeNodeForArrayCompare {
-    JSValue value;
-
-    // Child pointers.  The high bit of gt is robbed and used as the
-    // balance factor sign.  The high bit of lt is robbed and used as
-    // the magnitude of the balance factor.
-    int32_t gt;
-    int32_t lt;
-};
-
-struct AVLTreeAbstractorForArrayCompare {
-    typedef int32_t handle; // Handle is an index into m_nodes vector.
-    typedef JSValue key;
-    typedef int32_t size;
-
-    Vector<AVLTreeNodeForArrayCompare, 0, UnsafeVectorOverflow> m_nodes;
-    ExecState* m_exec;
-    JSValue m_compareFunction;
-    CallType m_compareCallType;
-    const CallData* m_compareCallData;
-    OwnPtr<CachedCall> m_cachedCall;
-
-    handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
-    void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
-    handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
-    void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
-
-    int get_balance_factor(handle h)
-    {
-        if (m_nodes[h].gt & 0x80000000)
-            return -1;
-        return static_cast<unsigned>(m_nodes[h].lt) >> 31;
-    }
-
-    void set_balance_factor(handle h, int bf)
-    {
-        if (bf == 0) {
-            m_nodes[h].lt &= 0x7FFFFFFF;
-            m_nodes[h].gt &= 0x7FFFFFFF;
-        } else {
-            m_nodes[h].lt |= 0x80000000;
-            if (bf < 0)
-                m_nodes[h].gt |= 0x80000000;
-            else
-                m_nodes[h].gt &= 0x7FFFFFFF;
-        }
-    }
-
-    int compare_key_key(key va, key vb)
-    {
-        ASSERT(!va.isUndefined());
-        ASSERT(!vb.isUndefined());
-
-        if (m_exec->hadException())
-            return 1;
-
-        double compareResult;
-        if (m_cachedCall) {
-            m_cachedCall->setThis(jsUndefined());
-            m_cachedCall->setArgument(0, va);
-            m_cachedCall->setArgument(1, vb);
-            compareResult = m_cachedCall->call().toNumber(m_exec);
-        } else {
-            MarkedArgumentBuffer arguments;
-            arguments.append(va);
-            arguments.append(vb);
-            compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, jsUndefined(), arguments).toNumber(m_exec);
-        }
-        return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
-    }
-
-    int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
-    int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
-
-    static handle null() { return 0x7FFFFFFF; }
-};
-
-template<IndexingType indexingType>
-void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
-    ASSERT(!inSparseIndexingMode());
-    ASSERT(indexingType == structure()->indexingType());
-    
-    // FIXME: This ignores exceptions raised in the compare function or in toNumber.
-        
-    // The maximum tree depth is compiled in - but the caller is clearly up to no good
-    // if a larger array is passed.
-    ASSERT(m_butterfly->publicLength() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
-    if (m_butterfly->publicLength() > static_cast<unsigned>(std::numeric_limits<int>::max()))
-        return;
-        
-    unsigned usedVectorLength = relevantLength<indexingType>();
-    unsigned nodeCount = usedVectorLength;
-        
-    if (!nodeCount)
-        return;
-        
-    AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
-    tree.abstractor().m_exec = exec;
-    tree.abstractor().m_compareFunction = compareFunction;
-    tree.abstractor().m_compareCallType = callType;
-    tree.abstractor().m_compareCallData = &callData;
-    tree.abstractor().m_nodes.grow(nodeCount);
-        
-    if (callType == CallTypeJS)
-        tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
-        
-    if (!tree.abstractor().m_nodes.begin()) {
-        throwOutOfMemoryError(exec);
-        return;
-    }
-        
-    // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
-    // right out from under us while we're building the tree here.
-        
-    unsigned numDefined = 0;
-    unsigned numUndefined = 0;
-    
-    // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
-    for (; numDefined < usedVectorLength; ++numDefined) {
-        if (numDefined >= m_butterfly->vectorLength())
-            break;
-        JSValue v = getHolyIndexQuickly(numDefined);
-        if (!v || v.isUndefined())
-            break;
-        tree.abstractor().m_nodes[numDefined].value = v;
-        tree.insert(numDefined);
-    }
-    for (unsigned i = numDefined; i < usedVectorLength; ++i) {
-        if (i >= m_butterfly->vectorLength())
-            break;
-        JSValue v = getHolyIndexQuickly(i);
-        if (v) {
-            if (v.isUndefined())
-                ++numUndefined;
-            else {
-                tree.abstractor().m_nodes[numDefined].value = v;
-                tree.insert(numDefined);
-                ++numDefined;
-            }
-        }
-    }
-    
-    unsigned newUsedVectorLength = numDefined + numUndefined;
-        
-    // The array size may have changed. Figure out the new bounds.
-    unsigned newestUsedVectorLength = currentRelevantLength();
-        
-    unsigned elementsToExtractThreshold = min(min(newestUsedVectorLength, numDefined), static_cast<unsigned>(tree.abstractor().m_nodes.size()));
-    unsigned undefinedElementsThreshold = min(newestUsedVectorLength, newUsedVectorLength);
-    unsigned clearElementsThreshold = min(newestUsedVectorLength, usedVectorLength);
-        
-    // Copy the values back into m_storage.
-    AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
-    iter.start_iter_least(tree);
-    VM& vm = exec->vm();
-    for (unsigned i = 0; i < elementsToExtractThreshold; ++i) {
-        ASSERT(i < butterfly()->vectorLength());
-        if (structure()->indexingType() == ArrayWithDouble)
-            butterfly()->contiguousDouble()[i] = tree.abstractor().m_nodes[*iter].value.asNumber();
-        else
-            currentIndexingData()[i].set(vm, this, tree.abstractor().m_nodes[*iter].value);
-        ++iter;
-    }
-    // Put undefined values back in.
-    switch (structure()->indexingType()) {
-    case ArrayWithInt32:
-    case ArrayWithDouble:
-        ASSERT(elementsToExtractThreshold == undefinedElementsThreshold);
-        break;
-        
-    default:
-        for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) {
-            ASSERT(i < butterfly()->vectorLength());
-            currentIndexingData()[i].setUndefined();
-        }
-    }
-
-    // Ensure that unused values in the vector are zeroed out.
-    for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) {
-        ASSERT(i < butterfly()->vectorLength());
-        if (structure()->indexingType() == ArrayWithDouble)
-            butterfly()->contiguousDouble()[i] = QNaN;
-        else
-            currentIndexingData()[i].clear();
-    }
-    
-    if (hasArrayStorage(structure()->indexingType()))
-        arrayStorage()->m_numValuesInVector = newUsedVectorLength;
-}
-
-void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
-    ASSERT(!inSparseIndexingMode());
-    
-    switch (structure()->indexingType()) {
-    case ArrayClass:
-    case ArrayWithUndecided:
-        return;
-        
-    case ArrayWithInt32:
-        sortVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
-        return;
-
-    case ArrayWithDouble:
-        sortVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
-        return;
-
-    case ArrayWithContiguous:
-        sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
-        return;
-
-    case ArrayWithArrayStorage:
-        sortVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
-        return;
-        
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-    }
-}
-
 void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
 {
     unsigned i = 0;
     unsigned vectorEnd;
     WriteBarrier<Unknown>* vector;
+
+    Butterfly* butterfly = m_butterfly.get();
     
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return;
         
@@ -1498,16 +1267,16 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
         
     case ArrayWithInt32:
     case ArrayWithContiguous: {
-        vectorEnd = m_butterfly->publicLength();
-        vector = m_butterfly->contiguous().data();
+        vectorEnd = butterfly->publicLength();
+        vector = butterfly->contiguous().data();
         break;
     }
         
     case ArrayWithDouble: {
         vector = 0;
         vectorEnd = 0;
-        for (; i < m_butterfly->publicLength(); ++i) {
-            double v = butterfly()->contiguousDouble()[i];
+        for (; i < butterfly->publicLength(); ++i) {
+            double v = butterfly->contiguousDouble()[i];
             if (v != v)
                 break;
             args.append(JSValue(JSValue::EncodeAsDouble, v));
@@ -1516,7 +1285,7 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
     }
     
     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
-        ArrayStorage* storage = m_butterfly->arrayStorage();
+        ArrayStorage* storage = butterfly->arrayStorage();
         
         vector = storage->m_vector;
         vectorEnd = min(storage->length(), storage->vectorLength());
@@ -1525,9 +1294,11 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
         
     default:
         CRASH();
+#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
         vector = 0;
         vectorEnd = 0;
         break;
+#endif
     }
     
     for (; i < vectorEnd; ++i) {
@@ -1536,19 +1307,27 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
             break;
         args.append(v.get());
     }
-    
+
+    // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
     for (; i < length(); ++i)
         args.append(get(exec, i));
 }
 
-void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
+void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
 {
-    unsigned i = 0;
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    unsigned i = offset;
     WriteBarrier<Unknown>* vector;
     unsigned vectorEnd;
-    
+    length += offset; // We like to think of the length as being our length, rather than the output length.
+
+    // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
     ASSERT(length == this->length());
-    switch (structure()->indexingType()) {
+
+    Butterfly* butterfly = m_butterfly.get();
+    switch (indexingType()) {
     case ArrayClass:
         return;
         
@@ -1560,26 +1339,26 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le
         
     case ArrayWithInt32:
     case ArrayWithContiguous: {
-        vector = m_butterfly->contiguous().data();
-        vectorEnd = m_butterfly->publicLength();
+        vector = butterfly->contiguous().data();
+        vectorEnd = butterfly->publicLength();
         break;
     }
         
     case ArrayWithDouble: {
         vector = 0;
         vectorEnd = 0;
-        for (; i < m_butterfly->publicLength(); ++i) {
-            ASSERT(i < butterfly()->vectorLength());
-            double v = m_butterfly->contiguousDouble()[i];
+        for (; i < butterfly->publicLength(); ++i) {
+            ASSERT(i < butterfly->vectorLength());
+            double v = butterfly->contiguousDouble()[i];
             if (v != v)
                 break;
-            callFrame->setArgument(i, JSValue(JSValue::EncodeAsDouble, v));
+            exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
         }
         break;
     }
         
     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
-        ArrayStorage* storage = m_butterfly->arrayStorage();
+        ArrayStorage* storage = butterfly->arrayStorage();
         vector = storage->m_vector;
         vectorEnd = min(length, storage->vectorLength());
         break;
@@ -1587,111 +1366,24 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le
         
     default:
         CRASH();
+#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
         vector = 0;
         vectorEnd = 0;
         break;
+#endif
     }
     
     for (; i < vectorEnd; ++i) {
         WriteBarrier<Unknown>& v = vector[i];
         if (!v)
             break;
-        callFrame->setArgument(i, v.get());
+        exec->r(firstElementDest + i - offset) = v.get();
     }
     
-    for (; i < length; ++i)
-        callFrame->setArgument(i, get(exec, i));
-}
-
-template<IndexingType indexingType>
-void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLength)
-{
-    ASSERT(!inSparseIndexingMode());
-    ASSERT(indexingType == structure()->indexingType());
-
-    unsigned myRelevantLength = relevantLength<indexingType>();
-    
-    numDefined = 0;
-    unsigned numUndefined = 0;
-        
-    for (; numDefined < myRelevantLength; ++numDefined) {
-        ASSERT(numDefined < m_butterfly->vectorLength());
-        if (indexingType == ArrayWithInt32) {
-            JSValue v = m_butterfly->contiguousInt32()[numDefined].get();
-            if (!v)
-                break;
-            ASSERT(v.isInt32());
-            continue;
-        }
-        if (indexingType == ArrayWithDouble) {
-            double v = m_butterfly->contiguousDouble()[numDefined];
-            if (v != v)
-                break;
-            continue;
-        }
-        JSValue v = indexingData<indexingType>()[numDefined].get();
-        if (!v || v.isUndefined())
-            break;
-    }
-        
-    for (unsigned i = numDefined; i < myRelevantLength; ++i) {
-        ASSERT(i < m_butterfly->vectorLength());
-        if (indexingType == ArrayWithInt32) {
-            JSValue v = m_butterfly->contiguousInt32()[i].get();
-            if (!v)
-                continue;
-            ASSERT(v.isInt32());
-            ASSERT(numDefined < m_butterfly->vectorLength());
-            m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v);
-            continue;
-        }
-        if (indexingType == ArrayWithDouble) {
-            double v = m_butterfly->contiguousDouble()[i];
-            if (v != v)
-                continue;
-            ASSERT(numDefined < m_butterfly->vectorLength());
-            m_butterfly->contiguousDouble()[numDefined++] = v;
-            continue;
-        }
-        JSValue v = indexingData<indexingType>()[i].get();
-        if (v) {
-            if (v.isUndefined())
-                ++numUndefined;
-            else {
-                ASSERT(numDefined < m_butterfly->vectorLength());
-                indexingData<indexingType>()[numDefined++].setWithoutWriteBarrier(v);
-            }
-        }
-    }
-        
-    newRelevantLength = numDefined + numUndefined;
-    
-    if (hasArrayStorage(indexingType))
-        RELEASE_ASSERT(!arrayStorage()->m_sparseMap);
-    
-    switch (indexingType) {
-    case ArrayWithInt32:
-    case ArrayWithDouble:
-        RELEASE_ASSERT(numDefined == newRelevantLength);
-        break;
-        
-    default:
-        for (unsigned i = numDefined; i < newRelevantLength; ++i) {
-            ASSERT(i < m_butterfly->vectorLength());
-            indexingData<indexingType>()[i].setUndefined();
-        }
-        break;
+    for (; i < length; ++i) {
+        exec->r(firstElementDest + i - offset) = get(exec, i);
+        RETURN_IF_EXCEPTION(scope, void());
     }
-    for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) {
-        ASSERT(i < m_butterfly->vectorLength());
-        if (indexingType == ArrayWithDouble)
-            m_butterfly->contiguousDouble()[i] = QNaN;
-        else
-            indexingData<indexingType>()[i].clear();
-    }
-
-    if (hasArrayStorage(indexingType))
-        arrayStorage()->m_numValuesInVector = newRelevantLength;
 }
 
 } // namespace JSC