]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/JSArray.cpp
JavaScriptCore-7600.1.4.17.5.tar.gz
[apple/javascriptcore.git] / runtime / JSArray.cpp
index f97ccedcd0d7a9153bce7801d606eab17169e412..f4bc093deedf1a176a369cde6aa97347602fea08 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003, 2007, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
  *
 #include "ButterflyInlines.h"
 #include "CachedCall.h"
 #include "CopiedSpace.h"
-#include "CopiedSpaceInlines.h"
 #include "Error.h"
 #include "Executable.h"
 #include "GetterSetter.h"
 #include "IndexingHeaderInlines.h"
+#include "JSCInlines.h"
 #include "PropertyNameArray.h"
 #include "Reject.h"
 #include <wtf/AVLTree.h>
 #include <wtf/Assertions.h>
 #include <wtf/OwnPtr.h>
-#include <Operations.h>
 
 using namespace std;
 using namespace WTF;
 
 namespace JSC {
 
-ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
 
 const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
 
-Butterfly* createArrayButterflyInDictionaryIndexingMode(VM& vm, unsigned initialLength)
+Butterfly* createArrayButterflyInDictionaryIndexingMode(
+    VM& vm, JSCell* intendedOwner, unsigned initialLength)
 {
     Butterfly* butterfly = Butterfly::create(
-        vm, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
+        vm, intendedOwner, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
     ArrayStorage* storage = butterfly->arrayStorage();
     storage->setLength(initialLength);
     storage->setVectorLength(0);
@@ -75,7 +75,7 @@ void JSArray::setLengthWritable(ExecState* exec, bool writable)
 }
 
 // Defined in ES5.1 15.4.5.1
-bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
+bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
 {
     JSArray* array = jsCast<JSArray*>(object);
 
@@ -108,7 +108,7 @@ 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)) {
-            throwError(exec, createRangeError(exec, "Invalid array length"));
+            exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
             return false;
         }
 
@@ -176,26 +176,16 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
     return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
 }
 
-bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
-    JSArray* thisObject = jsCast<JSArray*>(cell);
-    if (propertyName == exec->propertyNames().length) {
-        slot.setValue(jsNumber(thisObject->length()));
-        return true;
-    }
-
-    return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
-}
-
-bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
+bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
     JSArray* thisObject = jsCast<JSArray*>(object);
     if (propertyName == exec->propertyNames().length) {
-        descriptor.setDescriptor(jsNumber(thisObject->length()), thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
+        unsigned attributes = thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly;
+        slot.setValue(thisObject, attributes, jsNumber(thisObject->length()));
         return true;
     }
 
-    return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
+    return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
 }
 
 // ECMA 15.4.5.1
@@ -206,7 +196,7 @@ void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSVa
     if (propertyName == exec->propertyNames().length) {
         unsigned newLength = value.toUInt32(exec);
         if (value.toNumber(exec) != static_cast<double>(newLength)) {
-            throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
+            exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
             return;
         }
         thisObject->setLength(exec, newLength, slot.isStrictMode());
@@ -248,8 +238,8 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
 {
     ArrayStorage* storage = ensureArrayStorage(vm);
     Butterfly* butterfly = storage->butterfly();
-    unsigned propertyCapacity = structure()->outOfLineCapacity();
-    unsigned propertySize = structure()->outOfLineSize();
+    unsigned propertyCapacity = structure(vm)->outOfLineCapacity();
+    unsigned propertySize = structure(vm)->outOfLineSize();
 
     // If not, we should have handled this on the fast path.
     ASSERT(!addToFront || count > storage->m_indexBias);
@@ -278,15 +268,16 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
     // 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;
     // 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(vm));
         newStorageCapacity = currentCapacity;
     } else {
         size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
-        if (!vm.heap.tryAllocateStorage(newSize, &newAllocBase))
+        if (!vm.heap.tryAllocateStorage(this, newSize, &newAllocBase))
             return false;
         newStorageCapacity = desiredCapacity;
     }
@@ -306,7 +297,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(vm)) || postCapacity < storage->vectorLength() - length);
     }
 
     unsigned newVectorLength = requiredVectorLength + postCapacity;
@@ -318,7 +309,7 @@ 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)) {
+    } else if ((newAllocBase != butterfly->base(structure(vm))) || (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);
 
@@ -329,8 +320,7 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
 
     newButterfly->arrayStorage()->setVectorLength(newVectorLength);
     newButterfly->arrayStorage()->m_indexBias = newIndexBias;
-
-    m_butterfly = newButterfly;
+    setButterflyWithoutChangingStructure(vm, newButterfly);
 
     return true;
 }
@@ -401,7 +391,7 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
 
 bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
 {
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         if (!newLength)
             return true;
@@ -430,9 +420,9 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
             ensureLength(exec->vm(), newLength);
             return true;
         }
-        if (structure()->indexingType() == ArrayWithDouble) {
+        if (indexingType() == ArrayWithDouble) {
             for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
-                m_butterfly->contiguousDouble()[i] = QNaN;
+                m_butterfly->contiguousDouble()[i] = PNaN;
         } else {
             for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
                 m_butterfly->contiguous()[i].clear();
@@ -452,7 +442,7 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
 
 JSValue JSArray::pop(ExecState* exec)
 {
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return jsUndefined();
         
@@ -488,7 +478,7 @@ JSValue JSArray::pop(ExecState* exec)
         RELEASE_ASSERT(length < m_butterfly->vectorLength());
         double value = m_butterfly->contiguousDouble()[length];
         if (value == value) {
-            m_butterfly->contiguousDouble()[length] = QNaN;
+            m_butterfly->contiguousDouble()[length] = PNaN;
             m_butterfly->setPublicLength(length);
             return JSValue(JSValue::EncodeAsDouble, value);
         }
@@ -547,10 +537,10 @@ 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()) {
+    switch (indexingType()) {
     case ArrayClass: {
         createInitialUndecided(exec->vm(), 0);
-        // Fall through.
+        FALLTHROUGH;
     }
         
     case ArrayWithUndecided: {
@@ -575,9 +565,9 @@ void JSArray::push(ExecState* exec, JSValue value)
         }
         
         if (length > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, length, value, true);
+            methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
             if (!exec->hadException())
-                throwError(exec, createRangeError(exec, "Invalid array length"));
+                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
             return;
         }
         
@@ -595,9 +585,9 @@ void JSArray::push(ExecState* exec, JSValue value)
         }
         
         if (length > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, length, value, true);
+            methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
             if (!exec->hadException())
-                throwError(exec, createRangeError(exec, "Invalid array length"));
+                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
             return;
         }
         
@@ -627,9 +617,9 @@ void JSArray::push(ExecState* exec, JSValue value)
         }
         
         if (length > MAX_ARRAY_INDEX) {
-            methodTable()->putByIndex(this, exec, length, value, true);
+            methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
             if (!exec->hadException())
-                throwError(exec, createRangeError(exec, "Invalid array length"));
+                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
             return;
         }
         
@@ -644,7 +634,7 @@ void JSArray::push(ExecState* exec, JSValue value)
                 setLength(exec, oldLength + 1, true);
             return;
         }
-        // Fall through.
+        FALLTHROUGH;
     }
         
     case ArrayWithArrayStorage: {
@@ -661,10 +651,10 @@ void JSArray::push(ExecState* exec, JSValue value)
 
         // 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);
+            methodTable(exec->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())
-                throwError(exec, createRangeError(exec, "Invalid array length"));
+                exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
             return;
         }
 
@@ -678,15 +668,18 @@ void JSArray::push(ExecState* exec, JSValue value)
     }
 }
 
-bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage* storage)
+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)) 
+        || inSparseIndexingMode() 
+        || shouldUseSlowPut(indexingType())) {
         return false;
+    }
 
     if (!oldLength)
         return true;
@@ -708,37 +701,83 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
     
     unsigned usedVectorLength = min(vectorLength, oldLength);
     
-    vectorLength -= count;
-    storage->setVectorLength(vectorLength);
-    
-    if (vectorLength) {
-        if (startIndex < usedVectorLength - (startIndex + count)) {
-            if (startIndex) {
-                memmove(
-                    storage->m_vector + count,
+    unsigned numElementsBeforeShiftRegion = startIndex;
+    unsigned firstIndexAfterShiftRegion = startIndex + count;
+    unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
+    ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
+
+    // The point of this comparison seems to be to minimize the amount of elements that have to 
+    // be moved during a shift operation.
+    if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
+        // The number of elements before the shift region is less than the number of elements
+        // after the shift region, so we move the elements before to the right.
+        if (numElementsBeforeShiftRegion) {
+            RELEASE_ASSERT(count + startIndex <= vectorLength);
+            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);
             }
-            m_butterfly = m_butterfly->shift(structure(), count);
-            storage = m_butterfly->arrayStorage();
-            storage->m_indexBias += count;
+        }
+        // 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();
+        storage->m_indexBias += count;
+
+        // Since we're consuming part of the vector by moving its beginning to the left,
+        // we need to modify the vector length appropriately.
+        storage->setVectorLength(vectorLength - count);
+    } 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.
+        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 + startIndex + count,
-                sizeof(JSValue) * (usedVectorLength - (startIndex + count)));
-            for (unsigned i = usedVectorLength - count; i < usedVectorLength; ++i)
-                storage->m_vector[i].clear();
+            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)
+            storage->m_vector[i].clear();
+        // We don't modify the index bias or the Butterfly pointer in this case because we're not changing 
+        // the start of the Butterfly, which needs to point at the first indexed property in the used 
+        // portion of the vector. We also don't modify the vector length because we're not actually changing
+        // its length; we're just using less of it.
     }
+    
     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);
     
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return true;
         
@@ -754,27 +793,28 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
         // 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) {
-            // 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.
-            JSValue v = m_butterfly->contiguous()[i + count].get();
-            if (UNLIKELY(!v)) {
-                // The purpose of this path is to ensure that we don't make the same
-                // mistake in the future: shiftCountWithArrayStorage() can't do anything
-                // about holes (at least for now), but it can detect them quickly. So
-                // we convert to array storage and then allow the array storage path to
-                // figure it out.
-                return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+        if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
+            for (unsigned i = startIndex; i < end; ++i) {
+                JSValue v = m_butterfly->contiguous()[i + count].get();
+                if (UNLIKELY(!v)) {
+                    startIndex = i;
+                    return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
+                }
+                m_butterfly->contiguous()[i].setWithoutWriteBarrier(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);
+        } else {
+            memmove(m_butterfly->contiguous().data() + startIndex, 
+                m_butterfly->contiguous().data() + startIndex + count, 
+                sizeof(JSValue) * (end - startIndex));
         }
+
         for (unsigned i = end; i < oldLength; ++i)
             m_butterfly->contiguous()[i].clear();
         
@@ -789,29 +829,29 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
         // 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) {
-            // 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.
-            double v = m_butterfly->contiguousDouble()[i + count];
-            if (UNLIKELY(v != v)) {
-                // The purpose of this path is to ensure that we don't make the same
-                // mistake in the future: shiftCountWithArrayStorage() can't do anything
-                // about holes (at least for now), but it can detect them quickly. So
-                // we convert to array storage and then allow the array storage path to
-                // figure it out.
-                return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+        if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
+            for (unsigned i = startIndex; i < end; ++i) {
+                double v = m_butterfly->contiguousDouble()[i + count];
+                if (UNLIKELY(v != v)) {
+                    startIndex = i;
+                    return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
+                }
+                m_butterfly->contiguousDouble()[i] = 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;
+        } else {
+            memmove(m_butterfly->contiguousDouble().data() + startIndex,
+                m_butterfly->contiguousDouble().data() + startIndex + count,
+                sizeof(JSValue) * (end - startIndex));
         }
         for (unsigned i = end; i < oldLength; ++i)
-            m_butterfly->contiguousDouble()[i] = QNaN;
+            m_butterfly->contiguousDouble()[i] = PNaN;
         
         m_butterfly->setPublicLength(oldLength - count);
         return true;
@@ -819,7 +859,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
         
     case ArrayWithArrayStorage:
     case ArrayWithSlowPutArrayStorage:
-        return shiftCountWithArrayStorage(startIndex, count, arrayStorage());
+        return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
         
     default:
         CRASH();
@@ -836,7 +876,7 @@ bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex,
 
     // 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;
@@ -844,10 +884,11 @@ bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex,
     unsigned vectorLength = storage->vectorLength();
 
     if (moveFront && storage->m_indexBias >= count) {
-        m_butterfly = storage->butterfly()->unshift(structure(), count);
-        storage = m_butterfly->arrayStorage();
+        Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
+        storage = newButterfly->arrayStorage();
         storage->m_indexBias -= count;
         storage->setVectorLength(vectorLength + count);
+        setButterflyWithoutChangingStructure(exec->vm(), newButterfly);
     } else if (!moveFront && vectorLength - length >= count)
         storage = storage->butterfly()->arrayStorage();
     else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
@@ -873,7 +914,7 @@ bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex,
 
 bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
 {
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
     case ArrayWithUndecided:
         // We could handle this. But it shouldn't ever come up, so we won't.
@@ -889,11 +930,18 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
             return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
         
         ensureLength(exec->vm(), oldLength + count);
-        
+
+        // 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()));
+        }
+
+        for (unsigned i = oldLength; i-- > startIndex;) {
+            JSValue v = m_butterfly->contiguous()[i].get();
+            ASSERT(v);
             m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
         }
         
@@ -915,10 +963,17 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         
         ensureLength(exec->vm(), oldLength + count);
         
+        // 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()));
+        }
+
+        for (unsigned i = oldLength; i-- > startIndex;) {
+            double v = m_butterfly->contiguousDouble()[i];
+            ASSERT(v == v);
             m_butterfly->contiguousDouble()[i + count] = v;
         }
         
@@ -968,20 +1023,20 @@ static int compareByStringPairForQSort(const void* a, const void* b)
     return codePointCompare(va->second, vb->second);
 }
 
-template<IndexingType indexingType>
+template<IndexingType arrayIndexingType>
 void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
 {
-    ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage);
+    ASSERT(arrayIndexingType == ArrayWithInt32 || arrayIndexingType == ArrayWithDouble || arrayIndexingType == ArrayWithContiguous || arrayIndexingType == ArrayWithArrayStorage);
     
     unsigned lengthNotIncludingUndefined;
     unsigned newRelevantLength;
-    compactForSorting<indexingType>(
+    compactForSorting<arrayIndexingType>(
         lengthNotIncludingUndefined,
         newRelevantLength);
     
-    ContiguousJSValues data = indexingData<indexingType>();
+    ContiguousJSValues data = indexingData<arrayIndexingType>();
     
-    if (indexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
+    if (arrayIndexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
         throwOutOfMemoryError(exec);
         return;
     }
@@ -990,7 +1045,7 @@ void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallTy
         return;
     
     bool allValuesAreNumbers = true;
-    switch (indexingType) {
+    switch (arrayIndexingType) {
     case ArrayWithInt32:
     case ArrayWithDouble:
         break;
@@ -1012,7 +1067,7 @@ void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallTy
     // 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) {
+    switch (arrayIndexingType) {
     case ArrayWithInt32:
         compare = compareNumbersForQSortWithInt32;
         break;
@@ -1035,7 +1090,7 @@ void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType cal
 {
     ASSERT(!inSparseIndexingMode());
 
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return;
         
@@ -1088,7 +1143,7 @@ template <> struct ContiguousTypeAccessor<ArrayWithDouble> {
 };
 
 
-template<IndexingType indexingType, typename StorageType>
+template<IndexingType arrayIndexingType, typename StorageType>
 void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> data, unsigned relevantLength)
 {
     if (!relevantLength)
@@ -1112,11 +1167,11 @@ void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> d
     bool isSortingPrimitiveValues = true;
 
     for (size_t i = 0; i < relevantLength; i++) {
-        JSValue value = ContiguousTypeAccessor<indexingType>::getAsValue(data, i);
-        ASSERT(indexingType != ArrayWithInt32 || value.isInt32());
+        JSValue value = ContiguousTypeAccessor<arrayIndexingType>::getAsValue(data, i);
+        ASSERT(arrayIndexingType != ArrayWithInt32 || value.isInt32());
         ASSERT(!value.isUndefined());
         values[i].first = value;
-        if (indexingType != ArrayWithDouble && indexingType != ArrayWithInt32)
+        if (arrayIndexingType != ArrayWithDouble && arrayIndexingType != ArrayWithInt32)
             isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
     }
         
@@ -1147,7 +1202,7 @@ void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> d
     
     // 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) {
+    switch (arrayIndexingType) {
     case ArrayWithInt32:
     case ArrayWithDouble:
     case ArrayWithContiguous:
@@ -1157,7 +1212,7 @@ void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> d
     case ArrayWithArrayStorage:
         if (arrayStorage()->vectorLength() < relevantLength) {
             increaseVectorLength(exec->vm(), relevantLength);
-            ContiguousTypeAccessor<indexingType>::replaceDataReference(&data, arrayStorage()->vector());
+            ContiguousTypeAccessor<arrayIndexingType>::replaceDataReference(&data, arrayStorage()->vector());
         }
         if (arrayStorage()->length() < relevantLength)
             arrayStorage()->setLength(relevantLength);
@@ -1168,7 +1223,7 @@ void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> d
     }
 
     for (size_t i = 0; i < relevantLength; i++)
-        ContiguousTypeAccessor<indexingType>::setWithValue(vm, this, data, i, values[i].first);
+        ContiguousTypeAccessor<arrayIndexingType>::setWithValue(vm, this, data, i, values[i].first);
     
     Heap::heap(this)->popTempSortVector(&values);
 }
@@ -1177,7 +1232,7 @@ void JSArray::sort(ExecState* exec)
 {
     ASSERT(!inSparseIndexingMode());
     
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
     case ArrayWithUndecided:
         return;
@@ -1293,7 +1348,7 @@ struct AVLTreeAbstractorForArrayCompare {
             m_cachedCall->setThis(jsUndefined());
             m_cachedCall->setArgument(0, va);
             m_cachedCall->setArgument(1, vb);
-            compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec));
+            compareResult = m_cachedCall->call().toNumber(m_exec);
         } else {
             MarkedArgumentBuffer arguments;
             arguments.append(va);
@@ -1309,11 +1364,11 @@ struct AVLTreeAbstractorForArrayCompare {
     static handle null() { return 0x7FFFFFFF; }
 };
 
-template<IndexingType indexingType>
+template<IndexingType arrayIndexingType>
 void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
 {
     ASSERT(!inSparseIndexingMode());
-    ASSERT(indexingType == structure()->indexingType());
+    ASSERT(arrayIndexingType == indexingType());
     
     // FIXME: This ignores exceptions raised in the compare function or in toNumber.
         
@@ -1323,7 +1378,7 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call
     if (m_butterfly->publicLength() > static_cast<unsigned>(std::numeric_limits<int>::max()))
         return;
         
-    unsigned usedVectorLength = relevantLength<indexingType>();
+    unsigned usedVectorLength = relevantLength<arrayIndexingType>();
     unsigned nodeCount = usedVectorLength;
         
     if (!nodeCount)
@@ -1390,14 +1445,14 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call
     VM& vm = exec->vm();
     for (unsigned i = 0; i < elementsToExtractThreshold; ++i) {
         ASSERT(i < butterfly()->vectorLength());
-        if (structure()->indexingType() == ArrayWithDouble)
+        if (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()) {
+    switch (indexingType()) {
     case ArrayWithInt32:
     case ArrayWithDouble:
         ASSERT(elementsToExtractThreshold == undefinedElementsThreshold);
@@ -1413,13 +1468,13 @@ void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType call
     // 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;
+        if (indexingType() == ArrayWithDouble)
+            butterfly()->contiguousDouble()[i] = PNaN;
         else
             currentIndexingData()[i].clear();
     }
     
-    if (hasArrayStorage(structure()->indexingType()))
+    if (hasAnyArrayStorage(indexingType()))
         arrayStorage()->m_numValuesInVector = newUsedVectorLength;
 }
 
@@ -1427,7 +1482,7 @@ void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType,
 {
     ASSERT(!inSparseIndexingMode());
     
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
     case ArrayWithUndecided:
         return;
@@ -1459,7 +1514,7 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
     unsigned vectorEnd;
     WriteBarrier<Unknown>* vector;
     
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return;
         
@@ -1514,14 +1569,14 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
         args.append(get(exec, i));
 }
 
-void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
+void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t copyLength, int32_t firstVarArgOffset)
 {
-    unsigned i = 0;
+    unsigned i = firstVarArgOffset;
     WriteBarrier<Unknown>* vector;
     unsigned vectorEnd;
-    
+    unsigned length = copyLength + firstVarArgOffset;
     ASSERT(length == this->length());
-    switch (structure()->indexingType()) {
+    switch (indexingType()) {
     case ArrayClass:
         return;
         
@@ -1546,7 +1601,7 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le
             double v = m_butterfly->contiguousDouble()[i];
             if (v != v)
                 break;
-            callFrame->setArgument(i, JSValue(JSValue::EncodeAsDouble, v));
+            callFrame->setArgument(i - firstVarArgOffset, JSValue(JSValue::EncodeAsDouble, v));
         }
         break;
     }
@@ -1569,47 +1624,47 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le
         WriteBarrier<Unknown>& v = vector[i];
         if (!v)
             break;
-        callFrame->setArgument(i, v.get());
+        callFrame->setArgument(i - firstVarArgOffset, v.get());
     }
     
     for (; i < length; ++i)
-        callFrame->setArgument(i, get(exec, i));
+        callFrame->setArgument(i - firstVarArgOffset, get(exec, i));
 }
 
-template<IndexingType indexingType>
+template<IndexingType arrayIndexingType>
 void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLength)
 {
     ASSERT(!inSparseIndexingMode());
-    ASSERT(indexingType == structure()->indexingType());
+    ASSERT(arrayIndexingType == indexingType());
 
-    unsigned myRelevantLength = relevantLength<indexingType>();
+    unsigned myRelevantLength = relevantLength<arrayIndexingType>();
     
     numDefined = 0;
     unsigned numUndefined = 0;
         
     for (; numDefined < myRelevantLength; ++numDefined) {
         ASSERT(numDefined < m_butterfly->vectorLength());
-        if (indexingType == ArrayWithInt32) {
+        if (arrayIndexingType == ArrayWithInt32) {
             JSValue v = m_butterfly->contiguousInt32()[numDefined].get();
             if (!v)
                 break;
             ASSERT(v.isInt32());
             continue;
         }
-        if (indexingType == ArrayWithDouble) {
+        if (arrayIndexingType == ArrayWithDouble) {
             double v = m_butterfly->contiguousDouble()[numDefined];
             if (v != v)
                 break;
             continue;
         }
-        JSValue v = indexingData<indexingType>()[numDefined].get();
+        JSValue v = indexingData<arrayIndexingType>()[numDefined].get();
         if (!v || v.isUndefined())
             break;
     }
         
     for (unsigned i = numDefined; i < myRelevantLength; ++i) {
         ASSERT(i < m_butterfly->vectorLength());
-        if (indexingType == ArrayWithInt32) {
+        if (arrayIndexingType == ArrayWithInt32) {
             JSValue v = m_butterfly->contiguousInt32()[i].get();
             if (!v)
                 continue;
@@ -1618,7 +1673,7 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt
             m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v);
             continue;
         }
-        if (indexingType == ArrayWithDouble) {
+        if (arrayIndexingType == ArrayWithDouble) {
             double v = m_butterfly->contiguousDouble()[i];
             if (v != v)
                 continue;
@@ -1626,23 +1681,23 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt
             m_butterfly->contiguousDouble()[numDefined++] = v;
             continue;
         }
-        JSValue v = indexingData<indexingType>()[i].get();
+        JSValue v = indexingData<arrayIndexingType>()[i].get();
         if (v) {
             if (v.isUndefined())
                 ++numUndefined;
             else {
                 ASSERT(numDefined < m_butterfly->vectorLength());
-                indexingData<indexingType>()[numDefined++].setWithoutWriteBarrier(v);
+                indexingData<arrayIndexingType>()[numDefined++].setWithoutWriteBarrier(v);
             }
         }
     }
         
     newRelevantLength = numDefined + numUndefined;
     
-    if (hasArrayStorage(indexingType))
+    if (hasAnyArrayStorage(arrayIndexingType))
         RELEASE_ASSERT(!arrayStorage()->m_sparseMap);
     
-    switch (indexingType) {
+    switch (arrayIndexingType) {
     case ArrayWithInt32:
     case ArrayWithDouble:
         RELEASE_ASSERT(numDefined == newRelevantLength);
@@ -1651,19 +1706,19 @@ void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLengt
     default:
         for (unsigned i = numDefined; i < newRelevantLength; ++i) {
             ASSERT(i < m_butterfly->vectorLength());
-            indexingData<indexingType>()[i].setUndefined();
+            indexingData<arrayIndexingType>()[i].setUndefined();
         }
         break;
     }
     for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) {
         ASSERT(i < m_butterfly->vectorLength());
-        if (indexingType == ArrayWithDouble)
-            m_butterfly->contiguousDouble()[i] = QNaN;
+        if (arrayIndexingType == ArrayWithDouble)
+            m_butterfly->contiguousDouble()[i] = PNaN;
         else
-            indexingData<indexingType>()[i].clear();
+            indexingData<arrayIndexingType>()[i].clear();
     }
 
-    if (hasArrayStorage(indexingType))
+    if (hasAnyArrayStorage(arrayIndexingType))
         arrayStorage()->m_numValuesInVector = newRelevantLength;
 }