Rename AtomicString to AtomString
[WebKit-https.git] / Source / JavaScriptCore / runtime / ArrayPrototype.cpp
index c0d73e1..57a8695 100644 (file)
@@ -36,7 +36,7 @@
 #include "JSArrayInlines.h"
 #include "JSCBuiltins.h"
 #include "JSCInlines.h"
-#include "JSStringBuilder.h"
+#include "JSImmutableButterfly.h"
 #include "JSStringJoiner.h"
 #include "Lookup.h"
 #include "ObjectConstructor.h"
@@ -68,7 +68,6 @@ ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Str
 {
     ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
     prototype->finishCreation(vm, globalObject);
-    vm.heap.addFinalizer(prototype, destroy);
     return prototype;
 }
 
@@ -82,15 +81,15 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
-    vm.prototypeMap.addPrototype(this);
+    didBecomePrototype();
 
     putDirectWithoutTransition(vm, vm.propertyNames->toString, globalObject->arrayProtoToStringFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPublicName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
     putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
 
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("concat", arrayPrototypeConcatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", arrayPrototypeFillCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().concatPublicName(), arrayPrototypeConcatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().fillPublicName(), arrayPrototypeFillCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->join, arrayProtoFuncJoin, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("pop", arrayProtoFuncPop, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, ArrayPopIntrinsic);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().pushPublicName(), arrayProtoFuncPush, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayPushIntrinsic);
@@ -99,24 +98,26 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPublicName(), arrayProtoFuncShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().shiftPrivateName(), arrayProtoFuncShift, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly, 0);
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayProtoFuncSlice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2, ArraySliceIntrinsic);
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", arrayPrototypeSortCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().sortPublicName(), arrayPrototypeSortCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("splice", arrayProtoFuncSplice, static_cast<unsigned>(PropertyAttribute::DontEnum), 2);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("unshift", arrayProtoFuncUnShift, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("every", arrayPrototypeEveryCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("forEach", arrayPrototypeForEachCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("some", arrayPrototypeSomeCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().everyPublicName(), arrayPrototypeEveryCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().forEachPublicName(), arrayPrototypeForEachCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().somePublicName(), arrayPrototypeSomeCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("indexOf", arrayProtoFuncIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, ArrayIndexOfIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", arrayProtoFuncLastIndexOf, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduce", arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("reduceRight", arrayPrototypeReduceRightCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("map", arrayPrototypeMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().filterPublicName(), arrayPrototypeFilterCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().flatPublicName(), arrayPrototypeFlatCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().flatMapPublicName(), arrayPrototypeFlatMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().reducePublicName(), arrayPrototypeReduceCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().reduceRightPublicName(), arrayPrototypeReduceRightCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().mapPublicName(), arrayPrototypeMapCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), arrayPrototypeEntriesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().keysPublicName(), arrayPrototypeKeysCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", arrayPrototypeFindCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", arrayPrototypeFindIndexCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("includes", arrayPrototypeIncludesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
-    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("copyWithin", arrayPrototypeCopyWithinCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findPublicName(), arrayPrototypeFindCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().findIndexPublicName(), arrayPrototypeFindIndexCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().includesPublicName(), arrayPrototypeIncludesCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().copyWithinPublicName(), arrayPrototypeCopyWithinCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
 
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().entriesPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().entriesPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().forEachPrivateName(), getDirect(vm, vm.propertyNames->builtinNames().forEachPublicName()), static_cast<unsigned>(PropertyAttribute::ReadOnly));
@@ -124,27 +125,22 @@ void ArrayPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     putDirectWithoutTransition(vm, vm.propertyNames->builtinNames().valuesPrivateName(), globalObject->arrayProtoValuesFunction(), static_cast<unsigned>(PropertyAttribute::ReadOnly));
 
     JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
-    const char* unscopableNames[] = {
-        "copyWithin",
-        "entries",
-        "fill",
-        "find",
-        "findIndex",
-        "includes",
-        "keys",
-        "values"
+    unscopables->convertToDictionary(vm);
+    const Identifier* const unscopableNames[] = {
+        &vm.propertyNames->builtinNames().copyWithinPublicName(),
+        &vm.propertyNames->builtinNames().entriesPublicName(),
+        &vm.propertyNames->builtinNames().fillPublicName(),
+        &vm.propertyNames->builtinNames().findPublicName(),
+        &vm.propertyNames->builtinNames().findIndexPublicName(),
+        &vm.propertyNames->builtinNames().includesPublicName(),
+        &vm.propertyNames->builtinNames().keysPublicName(),
+        &vm.propertyNames->builtinNames().valuesPublicName()
     };
-    for (const char* unscopableName : unscopableNames)
-        unscopables->putDirect(vm, Identifier::fromString(&vm, unscopableName), jsBoolean(true));
+    for (const auto* unscopableName : unscopableNames)
+        unscopables->putDirect(vm, *unscopableName, jsBoolean(true));
     putDirectWithoutTransition(vm, vm.propertyNames->unscopablesSymbol, unscopables, PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
 }
 
-void ArrayPrototype::destroy(JSC::JSCell* cell)
-{
-    ArrayPrototype* thisObject = static_cast<ArrayPrototype*>(cell);
-    thisObject->ArrayPrototype::~ArrayPrototype();
-}
-
 // ------------------------------ Array Functions ----------------------------
 
 static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsigned index)
@@ -163,12 +159,10 @@ static ALWAYS_INLINE JSValue getProperty(ExecState* exec, JSObject* object, unsi
     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
     if (!hasProperty)
         return { };
-    if (UNLIKELY(slot.isTaintedByOpaqueObject())) {
-        scope.release();
-        return object->get(exec, index);
-    }
-    scope.release();
-    return slot.getValue(exec, index);
+    if (UNLIKELY(slot.isTaintedByOpaqueObject()))
+        RELEASE_AND_RETURN(scope, object->get(exec, index));
+
+    RELEASE_AND_RETURN(scope, slot.getValue(exec, index));
 }
 
 static ALWAYS_INLINE bool putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
@@ -188,21 +182,27 @@ static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsi
     bool success = putLength(exec, vm, obj, jsNumber(value));
     RETURN_IF_EXCEPTION(scope, void());
     if (UNLIKELY(!success))
-        throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
+        throwTypeError(exec, scope, ReadonlyPropertyWriteError);
+}
+
+namespace ArrayPrototypeInternal {
+static bool verbose = false;
 }
 
 ALWAYS_INLINE bool speciesWatchpointIsValid(ExecState* exec, JSObject* thisObject)
 {
-    JSGlobalObject* globalObject = thisObject->globalObject();
+    VM& vm = exec->vm();
+    JSGlobalObject* globalObject = thisObject->globalObject(vm);
     ArrayPrototype* arrayPrototype = globalObject->arrayPrototype();
 
     if (globalObject->arraySpeciesWatchpoint().stateOnJSThread() == ClearWatchpoint) {
-        arrayPrototype->tryInitializeSpeciesWatchpoint(exec);
+        dataLogLnIf(ArrayPrototypeInternal::verbose, "Initializing Array species watchpoints for Array.prototype: ", pointerDump(arrayPrototype), " with structure: ", pointerDump(arrayPrototype->structure(vm)), "\nand Array: ", pointerDump(globalObject->arrayConstructor()), " with structure: ", pointerDump(globalObject->arrayConstructor()->structure(vm)));
+        globalObject->tryInstallArraySpeciesWatchpoint(exec);
         ASSERT(globalObject->arraySpeciesWatchpoint().stateOnJSThread() != ClearWatchpoint);
     }
 
-    return !thisObject->hasCustomProperties()
-        && arrayPrototype == thisObject->getPrototypeDirect()
+    return !thisObject->hasCustomProperties(vm)
+        && arrayPrototype == thisObject->getPrototypeDirect(vm)
         && globalObject->arraySpeciesWatchpoint().stateOnJSThread() == IsWatched;
 }
 
@@ -235,9 +235,9 @@ static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstru
 
         constructor = thisObject->get(exec, vm.propertyNames->constructor);
         RETURN_IF_EXCEPTION(scope, exceptionResult());
-        if (constructor.isConstructor()) {
+        if (constructor.isConstructor(vm)) {
             JSObject* constructorObject = jsCast<JSObject*>(constructor);
-            if (exec->lexicalGlobalObject() != constructorObject->globalObject())
+            if (exec->lexicalGlobalObject() != constructorObject->globalObject(vm))
                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
         }
         if (constructor.isObject()) {
@@ -246,14 +246,17 @@ static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstru
             if (constructor.isNull())
                 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);;
         }
-    } else
-        RETURN_IF_EXCEPTION(scope, exceptionResult());
+    } else {
+        // If isArray is false, return ? ArrayCreate(length).
+        return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
+    }
 
     if (constructor.isUndefined())
         return std::make_pair(SpeciesConstructResult::FastPath, nullptr);
 
     MarkedArgumentBuffer args;
     args.append(jsNumber(length));
+    ASSERT(!args.hasOverflowed());
     JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor");
     RETURN_IF_EXCEPTION(scope, exceptionResult());
     return std::make_pair(SpeciesConstructResult::CreatedObject, newObject);
@@ -318,7 +321,7 @@ void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned current
             bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
             RETURN_IF_EXCEPTION(scope, void());
             if (!success) {
-                throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+                throwTypeError(exec, scope, UnableToDeletePropertyError);
                 return;
             }
         }
@@ -327,7 +330,7 @@ void shift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned current
         bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, k - 1);
         RETURN_IF_EXCEPTION(scope, void());
         if (!success) {
-            throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+            throwTypeError(exec, scope, UnableToDeletePropertyError);
             return;
         }
     }
@@ -346,7 +349,7 @@ void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned curre
     RELEASE_ASSERT(currentCount <= (length - header));
 
     // Guard against overflow.
-    if (count > (UINT_MAX - length)) {
+    if (count > UINT_MAX - length) {
         throwOutOfMemoryError(exec, scope);
         return;
     }
@@ -373,13 +376,162 @@ void unshift(ExecState* exec, JSObject* thisObj, unsigned header, unsigned curre
             bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, to);
             RETURN_IF_EXCEPTION(scope, void());
             if (UNLIKELY(!success)) {
-                throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+                throwTypeError(exec, scope, UnableToDeletePropertyError);
                 return;
             }
         }
     }
 }
 
+inline bool canUseFastJoin(const JSObject* thisObject)
+{
+    switch (thisObject->indexingType()) {
+    case ALL_CONTIGUOUS_INDEXING_TYPES:
+    case ALL_INT32_INDEXING_TYPES:
+    case ALL_DOUBLE_INDEXING_TYPES:
+    case ALL_UNDECIDED_INDEXING_TYPES:
+        return true;
+    default:
+        break;
+    }
+    return false;
+}
+
+inline bool holesMustForwardToPrototype(VM& vm, JSObject* object)
+{
+    return object->structure(vm)->holesMustForwardToPrototype(vm, object);
+}
+
+inline bool isHole(double value)
+{
+    return std::isnan(value);
+}
+
+inline bool isHole(const WriteBarrier<Unknown>& value)
+{
+    return !value;
+}
+
+template<typename T>
+inline bool containsHole(T* data, unsigned length)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if (isHole(data[i]))
+            return true;
+    }
+    return false;
+}
+
+inline JSValue fastJoin(ExecState& state, JSObject* thisObject, StringView separator, unsigned length, bool* sawHoles = nullptr)
+{
+    VM& vm = state.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    switch (thisObject->indexingType()) {
+    case ALL_INT32_INDEXING_TYPES: {
+        auto& butterfly = *thisObject->butterfly();
+        if (UNLIKELY(length > butterfly.publicLength()))
+            break;
+        JSStringJoiner joiner(state, separator, length);
+        RETURN_IF_EXCEPTION(scope, { });
+        auto data = butterfly.contiguous().data();
+        bool holesKnownToBeOK = false;
+        for (unsigned i = 0; i < length; ++i) {
+            JSValue value = data[i].get();
+            if (LIKELY(value))
+                joiner.appendNumber(vm, value.asInt32());
+            else {
+                if (sawHoles)
+                    *sawHoles = true;
+                if (!holesKnownToBeOK) {
+                    if (holesMustForwardToPrototype(vm, thisObject))
+                        goto generalCase;
+                    holesKnownToBeOK = true;
+                }
+                joiner.appendEmptyString();
+            }
+        }
+        RELEASE_AND_RETURN(scope, joiner.join(state));
+    }
+    case ALL_CONTIGUOUS_INDEXING_TYPES: {
+        auto& butterfly = *thisObject->butterfly();
+        if (UNLIKELY(length > butterfly.publicLength()))
+            break;
+        JSStringJoiner joiner(state, separator, length);
+        RETURN_IF_EXCEPTION(scope, { });
+        auto data = butterfly.contiguous().data();
+        bool holesKnownToBeOK = false;
+        for (unsigned i = 0; i < length; ++i) {
+            if (JSValue value = data[i].get()) {
+                if (!joiner.appendWithoutSideEffects(state, value))
+                    goto generalCase;
+            } else {
+                if (sawHoles)
+                    *sawHoles = true;
+                if (!holesKnownToBeOK) {
+                    if (holesMustForwardToPrototype(vm, thisObject))
+                        goto generalCase;
+                    holesKnownToBeOK = true;
+                }
+                joiner.appendEmptyString();
+            }
+        }
+        RELEASE_AND_RETURN(scope, joiner.join(state));
+    }
+    case ALL_DOUBLE_INDEXING_TYPES: {
+        auto& butterfly = *thisObject->butterfly();
+        if (UNLIKELY(length > butterfly.publicLength()))
+            break;
+        JSStringJoiner joiner(state, separator, length);
+        RETURN_IF_EXCEPTION(scope, { });
+        auto data = butterfly.contiguousDouble().data();
+        bool holesKnownToBeOK = false;
+        for (unsigned i = 0; i < length; ++i) {
+            double value = data[i];
+            if (LIKELY(!isHole(value)))
+                joiner.appendNumber(vm, value);
+            else {
+                if (sawHoles)
+                    *sawHoles = true;
+                if (!holesKnownToBeOK) {
+                    if (holesMustForwardToPrototype(vm, thisObject))
+                        goto generalCase;
+                    holesKnownToBeOK = true;
+                }
+                joiner.appendEmptyString();
+            }
+        }
+        RELEASE_AND_RETURN(scope, joiner.join(state));
+    }
+    case ALL_UNDECIDED_INDEXING_TYPES: {
+        if (length && holesMustForwardToPrototype(vm, thisObject))
+            goto generalCase;
+        switch (separator.length()) {
+        case 0:
+            RELEASE_AND_RETURN(scope, jsEmptyString(&state));
+        case 1: {
+            if (length <= 1)
+                RELEASE_AND_RETURN(scope, jsEmptyString(&state));
+            if (separator.is8Bit())
+                RELEASE_AND_RETURN(scope, repeatCharacter(state, separator.characters8()[0], length - 1));
+            RELEASE_AND_RETURN(scope, repeatCharacter(state, separator.characters16()[0], length - 1));
+        }
+        }
+    }
+    }
+
+generalCase:
+    JSStringJoiner joiner(state, separator, length);
+    RETURN_IF_EXCEPTION(scope, { });
+    for (unsigned i = 0; i < length; ++i) {
+        JSValue element = thisObject->getIndex(&state, i);
+        RETURN_IF_EXCEPTION(scope, { });
+        joiner.append(state, element);
+        RETURN_IF_EXCEPTION(scope, { });
+    }
+    RELEASE_AND_RETURN(scope, joiner.join(state));
+}
+
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -399,20 +551,16 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
     if (!function.isCell())
         customJoinCase = true;
     CallData callData;
-    CallType callType = getCallData(function, callData);
+    CallType callType = getCallData(vm, function, callData);
     if (callType == CallType::None)
         customJoinCase = true;
 
-    if (UNLIKELY(customJoinCase)) {
-        scope.release();
-        return JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(vm)->className(thisObject), "]"));
-    }
+    if (UNLIKELY(customJoinCase))
+        RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(exec, "[object ", thisObject->methodTable(vm)->className(thisObject, vm), "]")));
 
     // 4. Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.
-    if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin) {
-        scope.release();
-        return JSValue::encode(call(exec, function, callType, callData, thisObject, *vm.emptyList));
-    }
+    if (!isJSArray(thisObject) || callType != CallType::Host || callData.native.function != arrayProtoFuncJoin)
+        RELEASE_AND_RETURN(scope, JSValue::encode(call(exec, function, callType, callData, thisObject, *vm.emptyList)));
 
     ASSERT(isJSArray(thisValue));
     JSArray* thisArray = asArray(thisValue);
@@ -424,6 +572,30 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
     if (JSValue earlyReturnValue = checker.earlyReturnValue())
         return JSValue::encode(earlyReturnValue);
 
+    if (LIKELY(canUseFastJoin(thisArray))) {
+        const LChar comma = ',';
+        scope.release();
+
+        bool isCoW = isCopyOnWrite(thisArray->indexingMode());
+        JSImmutableButterfly* immutableButterfly = nullptr;
+        if (isCoW) {
+            immutableButterfly = JSImmutableButterfly::fromButterfly(thisArray->butterfly());
+            auto iter = vm.heap.immutableButterflyToStringCache.find(immutableButterfly);
+            if (iter != vm.heap.immutableButterflyToStringCache.end())
+                return JSValue::encode(iter->value);
+        }
+
+        bool sawHoles = false;
+        JSValue result = fastJoin(*exec, thisArray, { &comma, 1 }, length, &sawHoles);
+
+        if (!sawHoles && result && isJSString(result) && isCoW) {
+            ASSERT(JSImmutableButterfly::fromButterfly(thisArray->butterfly()) == immutableButterfly);
+            vm.heap.immutableButterflyToStringCache.add(immutableButterfly, jsCast<JSString*>(result));
+        }
+
+        return JSValue::encode(result);
+    }
+
     JSStringJoiner joiner(*exec, ',', length);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
@@ -437,8 +609,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
 
-    scope.release();
-    return JSValue::encode(joiner.join(*exec));
+    RELEASE_AND_RETURN(scope, JSValue::encode(joiner.join(*exec)));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
@@ -462,6 +633,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
 
 #if ENABLE(INTL)
     ArgList arguments(exec);
+#endif
     for (unsigned i = 0; i < length; ++i) {
         JSValue element = thisObject->getIndex(exec, i);
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
@@ -471,61 +643,21 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
             JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
             RETURN_IF_EXCEPTION(scope, encodedJSValue());
             CallData callData;
-            CallType callType = getCallData(conversionFunction, callData);
+            CallType callType = getCallData(vm, conversionFunction, callData);
             if (callType != CallType::None) {
+#if ENABLE(INTL)
                 element = call(exec, conversionFunction, callType, callData, element, arguments);
+#else
+                element = call(exec, conversionFunction, callType, callData, element, *vm.emptyList);
+#endif
                 RETURN_IF_EXCEPTION(scope, encodedJSValue());
             }
         }
         stringJoiner.append(*exec, element);
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
-#else // !ENABLE(INTL)
-    for (unsigned i = 0; i < length; ++i) {
-        JSValue element = thisObject->getIndex(exec, i);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        if (element.isUndefinedOrNull())
-            continue;
-        JSValue conversionFunction = element.get(exec, vm.propertyNames->toLocaleString);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        CallData callData;
-        CallType callType = getCallData(conversionFunction, callData);
-        if (callType != CallType::None) {
-            element = call(exec, conversionFunction, callType, callData, element, *vm.emptyList);
-            RETURN_IF_EXCEPTION(scope, encodedJSValue());
-        }
-        stringJoiner.append(*exec, element);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    }
-#endif // !ENABLE(INTL)
-
-    scope.release();
-    return JSValue::encode(stringJoiner.join(*exec));
-}
-
-static inline bool isHole(double value)
-{
-    return std::isnan(value);
-}
-
-static inline bool isHole(const WriteBarrier<Unknown>& value)
-{
-    return !value;
-}
-
-template<typename T> static inline bool containsHole(T* data, unsigned length)
-{
-    for (unsigned i = 0; i < length; ++i) {
-        if (isHole(data[i]))
-            return true;
-    }
-    return false;
-}
 
-static inline bool holesMustForwardToPrototype(ExecState& state, JSObject* object)
-{
-    auto& vm = state.vm();
-    return object->structure(vm)->holesMustForwardToPrototype(vm);
+    RELEASE_AND_RETURN(scope, JSValue::encode(stringJoiner.join(*exec)));
 }
 
 static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separator, uint64_t length)
@@ -554,7 +686,7 @@ static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separat
     // 9.e Increase k by 1..
     for (uint64_t k = 1; k < length; ++k) {
         // b. Let element be ? Get(O, ! ToString(k)).
-        JSValue element = thisObject->get(&exec, Identifier::fromString(&exec, AtomicString::number(k)));
+        JSValue element = thisObject->get(&exec, Identifier::fromString(&exec, AtomString::number(k)));
         RETURN_IF_EXCEPTION(scope, { });
 
         // c. If element is undefined or null, let next be the empty String; otherwise, let next be ? ToString(element).
@@ -576,89 +708,6 @@ static JSValue slowJoin(ExecState& exec, JSObject* thisObject, JSString* separat
     return r;
 }
 
-static inline bool canUseFastJoin(const JSObject* thisObject)
-{
-    switch (thisObject->indexingType()) {
-    case ALL_CONTIGUOUS_INDEXING_TYPES:
-    case ALL_INT32_INDEXING_TYPES:
-    case ALL_DOUBLE_INDEXING_TYPES:
-        return true;
-    default:
-        break;
-    }
-    return false;
-}
-
-static inline JSValue fastJoin(ExecState& state, JSObject* thisObject, StringView separator, unsigned length)
-{
-    VM& vm = state.vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    switch (thisObject->indexingType()) {
-    case ALL_CONTIGUOUS_INDEXING_TYPES:
-    case ALL_INT32_INDEXING_TYPES: {
-        auto& butterfly = *thisObject->butterfly();
-        if (length > butterfly.publicLength())
-            break;
-        JSStringJoiner joiner(state, separator, length);
-        RETURN_IF_EXCEPTION(scope, { });
-        auto data = butterfly.contiguous().data();
-        bool holesKnownToBeOK = false;
-        for (unsigned i = 0; i < length; ++i) {
-            if (JSValue value = data[i].get()) {
-                if (!joiner.appendWithoutSideEffects(state, value))
-                    goto generalCase;
-            } else {
-                if (!holesKnownToBeOK) {
-                    if (holesMustForwardToPrototype(state, thisObject))
-                        goto generalCase;
-                    holesKnownToBeOK = true;
-                }
-                joiner.appendEmptyString();
-            }
-        }
-        scope.release();
-        return joiner.join(state);
-    }
-    case ALL_DOUBLE_INDEXING_TYPES: {
-        auto& butterfly = *thisObject->butterfly();
-        if (length > butterfly.publicLength())
-            break;
-        JSStringJoiner joiner(state, separator, length);
-        RETURN_IF_EXCEPTION(scope, { });
-        auto data = butterfly.contiguousDouble().data();
-        bool holesKnownToBeOK = false;
-        for (unsigned i = 0; i < length; ++i) {
-            double value = data[i];
-            if (!isHole(value))
-                joiner.append(state, jsDoubleNumber(value));
-            else {
-                if (!holesKnownToBeOK) {
-                    if (thisObject->structure(vm)->holesMustForwardToPrototype(vm))
-                        goto generalCase;
-                    holesKnownToBeOK = true;
-                }
-                joiner.appendEmptyString();
-            }
-        }
-        scope.release();
-        return joiner.join(state);
-    }
-    }
-
-generalCase:
-    JSStringJoiner joiner(state, separator, length);
-    RETURN_IF_EXCEPTION(scope, { });
-    for (unsigned i = 0; i < length; ++i) {
-        JSValue element = thisObject->getIndex(&state, i);
-        RETURN_IF_EXCEPTION(scope, { });
-        joiner.append(state, element);
-        RETURN_IF_EXCEPTION(scope, { });
-    }
-    scope.release();
-    return joiner.join(state);
-}
-
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -690,15 +739,13 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
             JSString* jsSeparator = jsSingleCharacterString(exec, comma);
             RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-            scope.release();
-            return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
+            RELEASE_AND_RETURN(scope, JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64)));
         }
 
         unsigned unsignedLength = static_cast<unsigned>(length);
         ASSERT(static_cast<double>(unsignedLength) == length);
 
-        scope.release();
-        return JSValue::encode(fastJoin(*exec, thisObject, { &comma, 1 }, unsignedLength));
+        RELEASE_AND_RETURN(scope, JSValue::encode(fastJoin(*exec, thisObject, { &comma, 1 }, unsignedLength)));
     }
 
     // 4. Let sep be ? ToString(separator).
@@ -709,15 +756,13 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
         uint64_t length64 = static_cast<uint64_t>(length);
         ASSERT(static_cast<double>(length64) == length);
 
-        scope.release();
-        return JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64));
+        RELEASE_AND_RETURN(scope, JSValue::encode(slowJoin(*exec, thisObject, jsSeparator, length64)));
     }
 
     auto viewWithString = jsSeparator->viewWithUnderlyingString(exec);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    scope.release();
-    return JSValue::encode(fastJoin(*exec, thisObject, viewWithString.view, length));
+    RELEASE_AND_RETURN(scope, JSValue::encode(fastJoin(*exec, thisObject, viewWithString.view, length)));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
@@ -727,10 +772,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
 
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
 
-    if (isJSArray(thisValue)) {
-        scope.release();
-        return JSValue::encode(asArray(thisValue)->pop(exec));
-    }
+    if (isJSArray(thisValue))
+        RELEASE_AND_RETURN(scope, JSValue::encode(asArray(thisValue)->pop(exec)));
 
     JSObject* thisObj = thisValue.toObject(exec);
     EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
@@ -750,7 +793,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPop(ExecState* exec)
     bool success = thisObj->methodTable(vm)->deletePropertyByIndex(thisObj, exec, length - 1);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
     if (UNLIKELY(!success)) {
-        throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+        throwTypeError(exec, scope, UnableToDeletePropertyError);
         return encodedJSValue();
     }
     scope.release();
@@ -764,10 +807,10 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncPush(ExecState* exec)
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue thisValue = exec->thisValue().toThis(exec, StrictMode);
 
-    if (isJSArray(thisValue) && exec->argumentCount() == 1) {
+    if (LIKELY(isJSArray(thisValue) && exec->argumentCount() == 1)) {
         JSArray* array = asArray(thisValue);
         scope.release();
-        array->push(exec, exec->uncheckedArgument(0));
+        array->pushInline(exec, exec->uncheckedArgument(0));
         return JSValue::encode(jsNumber(array->length()));
     }
     
@@ -809,6 +852,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
     unsigned length = toLength(exec, thisObject);
     RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
+    thisObject->ensureWritable(vm);
+
     switch (thisObject->indexingType()) {
     case ALL_CONTIGUOUS_INDEXING_TYPES:
     case ALL_INT32_INDEXING_TYPES: {
@@ -816,9 +861,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
         if (length > butterfly.publicLength())
             break;
         auto data = butterfly.contiguous().data();
-        if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
+        if (containsHole(data, length) && holesMustForwardToPrototype(vm, thisObject))
             break;
         std::reverse(data, data + length);
+        if (!hasInt32(thisObject->indexingType()))
+            vm.heap.writeBarrier(thisObject);
         return JSValue::encode(thisObject);
     }
     case ALL_DOUBLE_INDEXING_TYPES: {
@@ -826,7 +873,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
         if (length > butterfly.publicLength())
             break;
         auto data = butterfly.contiguousDouble().data();
-        if (containsHole(data, length) && holesMustForwardToPrototype(*exec, thisObject))
+        if (containsHole(data, length) && holesMustForwardToPrototype(vm, thisObject))
             break;
         std::reverse(data, data + length);
         return JSValue::encode(thisObject);
@@ -835,10 +882,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
         auto& storage = *thisObject->butterfly()->arrayStorage();
         if (length > storage.vectorLength())
             break;
-        if (storage.hasHoles() && holesMustForwardToPrototype(*exec, thisObject))
+        if (storage.hasHoles() && holesMustForwardToPrototype(vm, thisObject))
             break;
         auto data = storage.vector().data();
         std::reverse(data, data + length);
+        vm.heap.writeBarrier(thisObject);
         return JSValue::encode(thisObject);
     }
     }
@@ -869,7 +917,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, lower);
             RETURN_IF_EXCEPTION(scope, encodedJSValue());
             if (UNLIKELY(!success)) {
-                throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+                throwTypeError(exec, scope, UnableToDeletePropertyError);
                 return encodedJSValue();
             }
         }
@@ -881,7 +929,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncReverse(ExecState* exec)
             bool success = thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, upper);
             RETURN_IF_EXCEPTION(scope, encodedJSValue());
             if (UNLIKELY(!success)) {
-                throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
+                throwTypeError(exec, scope, UnableToDeletePropertyError);
                 return encodedJSValue();
             }
         }
@@ -1085,7 +1133,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
     unsigned nrArgs = exec->argumentCount();
     if (nrArgs) {
         if (UNLIKELY(doubleLength + static_cast<double>(nrArgs) > maxSafeInteger()))
-            return throwVMTypeError(exec, scope, ASCIILiteral("Cannot shift to offset greater than (2 ** 53) - 1"));
+            return throwVMTypeError(exec, scope, "Cannot shift to offset greater than (2 ** 53) - 1"_s);
         unshift<JSArray::ShiftCountForShift>(exec, thisObj, 0, 0, nrArgs, length);
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
@@ -1099,30 +1147,140 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncUnShift(ExecState* exec)
     return JSValue::encode(result);
 }
 
+enum class IndexOfDirection { Forward, Backward };
+template<IndexOfDirection direction>
+ALWAYS_INLINE JSValue fastIndexOf(ExecState* exec, VM& vm, JSArray* array, unsigned length, JSValue searchElement, unsigned index)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    bool canDoFastPath = array->canDoFastIndexedAccess(vm)
+        && array->getArrayLength() == length; // The effects in getting `index` could have changed the length of this array.
+    if (!canDoFastPath)
+        return JSValue();
+
+    switch (array->indexingType()) {
+    case ALL_INT32_INDEXING_TYPES: {
+        if (!searchElement.isNumber())
+            return jsNumber(-1);
+        JSValue searchInt32;
+        if (searchElement.isInt32())
+            searchInt32 = searchElement;
+        else {
+            double searchNumber = searchElement.asNumber();
+            if (!canBeInt32(searchNumber))
+                return jsNumber(-1);
+            searchInt32 = jsNumber(static_cast<int32_t>(searchNumber));
+        }
+        auto& butterfly = *array->butterfly();
+        auto data = butterfly.contiguous().data();
+        if (direction == IndexOfDirection::Forward) {
+            for (; index < length; ++index) {
+                // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
+                // And the hole never matches against Int32 value.
+                if (searchInt32 == data[index].get())
+                    return jsNumber(index);
+            }
+        } else {
+            do {
+                ASSERT(index < length);
+                // Array#lastIndexOf uses `===` semantics (not HashMap isEqual semantics).
+                // And the hole never matches against Int32 value.
+                if (searchInt32 == data[index].get())
+                    return jsNumber(index);
+            } while (index--);
+        }
+        return jsNumber(-1);
+    }
+    case ALL_CONTIGUOUS_INDEXING_TYPES: {
+        auto& butterfly = *array->butterfly();
+        auto data = butterfly.contiguous().data();
+
+        if (direction == IndexOfDirection::Forward) {
+            for (; index < length; ++index) {
+                JSValue value = data[index].get();
+                if (!value)
+                    continue;
+                bool isEqual = JSValue::strictEqual(exec, searchElement, value);
+                RETURN_IF_EXCEPTION(scope, { });
+                if (isEqual)
+                    return jsNumber(index);
+            }
+        } else {
+            do {
+                ASSERT(index < length);
+                JSValue value = data[index].get();
+                if (!value)
+                    continue;
+                bool isEqual = JSValue::strictEqual(exec, searchElement, value);
+                RETURN_IF_EXCEPTION(scope, { });
+                if (isEqual)
+                    return jsNumber(index);
+            } while (index--);
+        }
+        return jsNumber(-1);
+    }
+    case ALL_DOUBLE_INDEXING_TYPES: {
+        if (!searchElement.isNumber())
+            return jsNumber(-1);
+        double searchNumber = searchElement.asNumber();
+        auto& butterfly = *array->butterfly();
+        auto data = butterfly.contiguousDouble().data();
+        if (direction == IndexOfDirection::Forward) {
+            for (; index < length; ++index) {
+                // Array#indexOf uses `===` semantics (not HashMap isEqual semantics).
+                // And the hole never matches since it is NaN.
+                if (data[index] == searchNumber)
+                    return jsNumber(index);
+            }
+        } else {
+            do {
+                ASSERT(index < length);
+                // Array#lastIndexOf uses `===` semantics (not HashMap isEqual semantics).
+                // And the hole never matches since it is NaN.
+                if (data[index] == searchNumber)
+                    return jsNumber(index);
+            } while (index--);
+        }
+        return jsNumber(-1);
+    }
+    default:
+        return JSValue();
+    }
+}
+
 EncodedJSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     // 15.4.4.14
-    JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
-    EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
-    if (UNLIKELY(!thisObj))
-        return encodedJSValue();
-    unsigned length = toLength(exec, thisObj);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
+    if (UNLIKELY(!thisObject))
+        return { };
+    unsigned length = toLength(exec, thisObject);
+    RETURN_IF_EXCEPTION(scope, { });
 
     unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    RETURN_IF_EXCEPTION(scope, { });
     JSValue searchElement = exec->argument(0);
+
+    if (isJSArray(thisObject)) {
+        JSValue result = fastIndexOf<IndexOfDirection::Forward>(exec, vm, asArray(thisObject), length, searchElement, index);
+        RETURN_IF_EXCEPTION(scope, { });
+        if (result)
+            return JSValue::encode(result);
+    }
+
     for (; index < length; ++index) {
-        JSValue e = getProperty(exec, thisObj, index);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        JSValue e = getProperty(exec, thisObject, index);
+        RETURN_IF_EXCEPTION(scope, { });
         if (!e)
             continue;
-        if (JSValue::strictEqual(exec, searchElement, e))
+        bool isEqual = JSValue::strictEqual(exec, searchElement, e);
+        RETURN_IF_EXCEPTION(scope, { });
+        if (isEqual)
             return JSValue::encode(jsNumber(index));
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
 
     return JSValue::encode(jsNumber(-1));
@@ -1134,11 +1292,11 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     // 15.4.4.15
-    JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
-    EXCEPTION_ASSERT(!!scope.exception() == !thisObj);
-    if (UNLIKELY(!thisObj))
-        return encodedJSValue();
-    unsigned length = toLength(exec, thisObj);
+    JSObject* thisObject = exec->thisValue().toThis(exec, StrictMode).toObject(exec);
+    EXCEPTION_ASSERT(!!scope.exception() == !thisObject);
+    if (UNLIKELY(!thisObject))
+        return { };
+    unsigned length = toLength(exec, thisObject);
     if (UNLIKELY(scope.exception()) || !length)
         return JSValue::encode(jsNumber(-1));
 
@@ -1146,6 +1304,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
     if (exec->argumentCount() >= 2) {
         JSValue fromValue = exec->uncheckedArgument(1);
         double fromDouble = fromValue.toInteger(exec);
+        RETURN_IF_EXCEPTION(scope, { });
         if (fromDouble < 0) {
             fromDouble += length;
             if (fromDouble < 0)
@@ -1156,15 +1315,24 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec)
     }
 
     JSValue searchElement = exec->argument(0);
+
+    if (isJSArray(thisObject)) {
+        JSValue result = fastIndexOf<IndexOfDirection::Backward>(exec, vm, asArray(thisObject), length, searchElement, index);
+        RETURN_IF_EXCEPTION(scope, { });
+        if (result)
+            return JSValue::encode(result);
+    }
+
     do {
-        RELEASE_ASSERT(index < length);
-        JSValue e = getProperty(exec, thisObj, index);
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        ASSERT(index < length);
+        JSValue e = getProperty(exec, thisObject, index);
+        RETURN_IF_EXCEPTION(scope, { });
         if (!e)
             continue;
-        if (JSValue::strictEqual(exec, searchElement, e))
+        bool isEqual = JSValue::strictEqual(exec, searchElement, e);
+        RETURN_IF_EXCEPTION(scope, { });
+        if (isEqual)
             return JSValue::encode(jsNumber(index));
-        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     } while (index--);
 
     return JSValue::encode(jsNumber(-1));
@@ -1174,7 +1342,7 @@ static bool moveElements(ExecState* exec, VM& vm, JSArray* target, unsigned targ
 {
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !source->structure()->holesMustForwardToPrototype(vm))) {
+    if (LIKELY(!hasAnyArrayStorage(source->indexingType()) && !holesMustForwardToPrototype(vm, source))) {
         for (unsigned i = 0; i < sourceLength; ++i) {
             JSValue value = source->tryGetIndexQuickly(i);
             if (value) {
@@ -1218,7 +1386,7 @@ static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, J
         type = first->indexingType();
 
     Structure* resultStructure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(type);
-    JSArray* result = JSArray::create(vm, resultStructure, resultSize);
+    JSArray* result = JSArray::tryCreate(vm, resultStructure, resultSize);
     if (UNLIKELY(!result)) {
         throwOutOfMemoryError(exec, scope);
         return encodedJSValue();
@@ -1241,6 +1409,29 @@ static EncodedJSValue concatAppendOne(ExecState* exec, VM& vm, JSArray* first, J
 
 }
 
+template<typename T>
+void clearElement(T& element)
+{
+    element.clear();
+}
+
+template<>
+void clearElement(double& element)
+{
+    element = PNaN;
+}
+
+template<typename T>
+ALWAYS_INLINE void copyElements(T* buffer, unsigned offset, void* source, unsigned sourceSize, IndexingType sourceType)
+{
+    if (sourceType != ArrayWithUndecided) {
+        memcpy(buffer + offset, source, sizeof(JSValue) * sourceSize);
+        return;
+    }
+
+    for (unsigned i = sourceSize; i--;)
+        clearElement<T>(buffer[i + offset]);
+};
 
 EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
 {
@@ -1263,10 +1454,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
         return JSValue::encode(jsNull());
 
     JSValue second = exec->uncheckedArgument(1);
-    if (!isJSArray(second)) {
-        scope.release();
-        return concatAppendOne(exec, vm, firstArray, second);
-    }
+    if (!isJSArray(second))
+        RELEASE_AND_RETURN(scope, concatAppendOne(exec, vm, firstArray, second));
 
     JSArray* secondArray = jsCast<JSArray*>(second);
     
@@ -1316,26 +1505,16 @@ EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec)
         throwOutOfMemoryError(exec, scope);
         return encodedJSValue();
     }
-    
+
     if (type == ArrayWithDouble) {
         double* buffer = result->butterfly()->contiguousDouble().data();
-        memcpy(buffer, firstButterfly->contiguousDouble().data(), sizeof(JSValue) * firstArraySize);
-        memcpy(buffer + firstArraySize, secondButterfly->contiguousDouble().data(), sizeof(JSValue) * secondArraySize);
+        copyElements(buffer, 0, firstButterfly->contiguousDouble().data(), firstArraySize, firstType);
+        copyElements(buffer, firstArraySize, secondButterfly->contiguousDouble().data(), secondArraySize, secondType);
+
     } else if (type != ArrayWithUndecided) {
         WriteBarrier<Unknown>* buffer = result->butterfly()->contiguous().data();
-        
-        auto copy = [&] (unsigned offset, void* source, unsigned size, IndexingType type) {
-            if (type != ArrayWithUndecided) {
-                memcpy(buffer + offset, source, sizeof(JSValue) * size);
-                return;
-            }
-            
-            for (unsigned i = size; i--;)
-                buffer[i + offset].clear();
-        };
-        
-        copy(0, firstButterfly->contiguous().data(), firstArraySize, firstType);
-        copy(firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
+        copyElements(buffer, 0, firstButterfly->contiguous().data(), firstArraySize, firstType);
+        copyElements(buffer, firstArraySize, secondButterfly->contiguous().data(), secondArraySize, secondType);
     }
 
     result->butterfly()->setPublicLength(resultSize);
@@ -1351,8 +1530,8 @@ EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
     JSArray* resultArray = jsCast<JSArray*>(exec->uncheckedArgument(0));
     JSArray* otherArray = jsCast<JSArray*>(exec->uncheckedArgument(1));
     JSValue startValue = exec->uncheckedArgument(2);
-    ASSERT(startValue.isAnyInt() && startValue.asAnyInt() >= 0 && startValue.asAnyInt() <= std::numeric_limits<unsigned>::max());
-    unsigned startIndex = static_cast<unsigned>(startValue.asAnyInt());
+    ASSERT(startValue.isUInt32AsAnyInt());
+    unsigned startIndex = startValue.asUInt32AsAnyInt();
     bool success = resultArray->appendMemcpy(exec, vm, startIndex, otherArray);
     EXCEPTION_ASSERT(!scope.exception() || !success);
     if (success)
@@ -1363,116 +1542,4 @@ EncodedJSValue JSC_HOST_CALL arrayProtoPrivateFuncAppendMemcpy(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
-
-// -------------------- ArrayPrototype.constructor Watchpoint ------------------
-
-namespace ArrayPrototypeInternal {
-static bool verbose = false;
-}
-
-class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {
-public:
-    typedef AdaptiveInferredPropertyValueWatchpointBase Base;
-    ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*);
-
-private:
-    void handleFire(const FireDetail&) override;
-
-    ArrayPrototype* m_arrayPrototype;
-};
-
-void ArrayPrototype::tryInitializeSpeciesWatchpoint(ExecState* exec)
-{
-    VM& vm = exec->vm();
-
-    RELEASE_ASSERT(!m_constructorWatchpoint);
-    RELEASE_ASSERT(!m_constructorSpeciesWatchpoint);
-
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    if (ArrayPrototypeInternal::verbose)
-        dataLog("Initializing Array species watchpoints for Array.prototype: ", pointerDump(this), " with structure: ", pointerDump(this->structure()), "\nand Array: ", pointerDump(this->globalObject()->arrayConstructor()), " with structure: ", pointerDump(this->globalObject()->arrayConstructor()->structure()), "\n");
-    // First we need to make sure that the Array.prototype.constructor property points to Array
-    // and that Array[Symbol.species] is the primordial GetterSetter.
-
-    // We only initialize once so flattening the structures does not have any real cost.
-    Structure* prototypeStructure = this->structure(vm);
-    if (prototypeStructure->isDictionary())
-        prototypeStructure = prototypeStructure->flattenDictionaryStructure(vm, this);
-    RELEASE_ASSERT(!prototypeStructure->isDictionary());
-
-    JSGlobalObject* globalObject = this->globalObject();
-    ArrayConstructor* arrayConstructor = globalObject->arrayConstructor();
-
-    auto invalidateWatchpoint = [&] {
-        globalObject->arraySpeciesWatchpoint().invalidate(vm, StringFireDetail("Was not able to set up array species watchpoint."));
-    };
-
-    PropertySlot constructorSlot(this, PropertySlot::InternalMethodType::VMInquiry);
-    this->getOwnPropertySlot(this, exec, vm.propertyNames->constructor, constructorSlot);
-    scope.assertNoException();
-    if (constructorSlot.slotBase() != this
-        || !constructorSlot.isCacheableValue()
-        || constructorSlot.getValue(exec, vm.propertyNames->constructor) != arrayConstructor) {
-        invalidateWatchpoint();
-        return;
-    }
-
-    Structure* constructorStructure = arrayConstructor->structure(vm);
-    if (constructorStructure->isDictionary())
-        constructorStructure = constructorStructure->flattenDictionaryStructure(vm, arrayConstructor);
-
-    PropertySlot speciesSlot(arrayConstructor, PropertySlot::InternalMethodType::VMInquiry);
-    arrayConstructor->getOwnPropertySlot(arrayConstructor, exec, vm.propertyNames->speciesSymbol, speciesSlot);
-    scope.assertNoException();
-    if (speciesSlot.slotBase() != arrayConstructor
-        || !speciesSlot.isCacheableGetter()
-        || speciesSlot.getterSetter() != globalObject->speciesGetterSetter()) {
-        invalidateWatchpoint();
-        return;
-    }
-
-    // Now we need to setup the watchpoints to make sure these conditions remain valid.
-    prototypeStructure->startWatchingPropertyForReplacements(vm, constructorSlot.cachedOffset());
-    constructorStructure->startWatchingPropertyForReplacements(vm, speciesSlot.cachedOffset());
-
-    ObjectPropertyCondition constructorCondition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), arrayConstructor);
-    ObjectPropertyCondition speciesCondition = ObjectPropertyCondition::equivalence(vm, this, arrayConstructor, vm.propertyNames->speciesSymbol.impl(), globalObject->speciesGetterSetter());
-
-    if (!constructorCondition.isWatchable() || !speciesCondition.isWatchable()) {
-        invalidateWatchpoint();
-        return;
-    }
-
-    m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(constructorCondition, this);
-    m_constructorWatchpoint->install();
-
-    m_constructorSpeciesWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(speciesCondition, this);
-    m_constructorSpeciesWatchpoint->install();
-
-    // We only watch this from the DFG, and the DFG makes sure to only start watching if the watchpoint is in the IsWatched state.
-    RELEASE_ASSERT(!globalObject->arraySpeciesWatchpoint().isBeingWatched()); 
-    globalObject->arraySpeciesWatchpoint().touch(vm, "Set up array species watchpoint.");
-}
-
-ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype)
-    : Base(key)
-    , m_arrayPrototype(prototype)
-{
-}
-
-void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail)
-{
-    StringPrintStream out;
-    out.print("ArrayPrototype adaption of ", key(), " failed: ", detail);
-
-    StringFireDetail stringDetail(out.toCString().data());
-
-    if (ArrayPrototypeInternal::verbose)
-        WTF::dataLog(stringDetail, "\n");
-
-    JSGlobalObject* globalObject = m_arrayPrototype->globalObject();
-    globalObject->arraySpeciesWatchpoint().fireAll(globalObject->vm(), stringDetail);
-}
-
 } // namespace JSC