String.prototype.replace() puts extra '<' in result when a named capture reference...
[WebKit-https.git] / Source / JavaScriptCore / runtime / StringPrototype.cpp
index 9339be5..97f79fa 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
- *  Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
+ *  Copyright (C) 2004-2017 Apple Inc. All rights reserved.
  *  Copyright (C) 2009 Torch Mobile, Inc.
  *  Copyright (C) 2015 Jordan Harband (ljharb@gmail.com)
  *
 #include "config.h"
 #include "StringPrototype.h"
 
+#include "BuiltinNames.h"
 #include "ButterflyInlines.h"
 #include "CachedCall.h"
-#include "CopiedSpaceInlines.h"
 #include "Error.h"
-#include "Executable.h"
+#include "FrameTracers.h"
 #include "IntlObject.h"
-#include "JSCInlines.h"
-#include "JSGlobalObjectFunctions.h"
 #include "JSArray.h"
+#include "JSCBuiltins.h"
+#include "JSCInlines.h"
 #include "JSFunction.h"
+#include "JSGlobalObjectFunctions.h"
 #include "JSStringBuilder.h"
 #include "JSStringIterator.h"
 #include "Lookup.h"
 #include "ObjectPrototype.h"
+#include "ParseInt.h"
 #include "PropertyNameArray.h"
 #include "RegExpCache.h"
 #include "RegExpConstructor.h"
-#include "RegExpMatchesArray.h"
 #include "RegExpObject.h"
+#include "SuperSampler.h"
 #include <algorithm>
 #include <unicode/uconfig.h>
 #include <unicode/unorm.h>
 #include <unicode/ustring.h>
 #include <wtf/ASCIICType.h>
 #include <wtf/MathExtras.h>
+#include <wtf/text/StringBuilder.h>
 #include <wtf/text/StringView.h>
 #include <wtf/unicode/Collator.h>
 
@@ -61,15 +64,11 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeat(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState*);
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState*);
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState*);
@@ -77,19 +76,6 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleLowerCase(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrim(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimLeft(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncTrimRight(ExecState*);
@@ -99,7 +85,39 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*);
 
-const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, 0, CREATE_METHOD_TABLE(StringPrototype) };
+}
+
+#include "StringPrototype.lut.h"
+
+namespace JSC {
+
+const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &stringPrototypeTable, nullptr, CREATE_METHOD_TABLE(StringPrototype) };
+
+/* Source for StringConstructor.lut.h
+@begin stringPrototypeTable
+    concat    JSBuiltin    DontEnum|Function 1
+    match     JSBuiltin    DontEnum|Function 1
+    padStart  JSBuiltin    DontEnum|Function 1
+    padEnd    JSBuiltin    DontEnum|Function 1
+    repeat    JSBuiltin    DontEnum|Function 1
+    replace   JSBuiltin    DontEnum|Function 2
+    search    JSBuiltin    DontEnum|Function 1
+    split     JSBuiltin    DontEnum|Function 1
+    anchor    JSBuiltin    DontEnum|Function 1
+    big       JSBuiltin    DontEnum|Function 0
+    bold      JSBuiltin    DontEnum|Function 0
+    blink     JSBuiltin    DontEnum|Function 0
+    fixed     JSBuiltin    DontEnum|Function 0
+    fontcolor JSBuiltin    DontEnum|Function 1
+    fontsize  JSBuiltin    DontEnum|Function 1
+    italics   JSBuiltin    DontEnum|Function 0
+    link      JSBuiltin    DontEnum|Function 1
+    small     JSBuiltin    DontEnum|Function 0
+    strike    JSBuiltin    DontEnum|Function 0
+    sub       JSBuiltin    DontEnum|Function 0
+    sup       JSBuiltin    DontEnum|Function 0
+@end
+*/
 
 // ECMA 15.5.4
 StringPrototype::StringPrototype(VM& vm, Structure* structure)
@@ -110,57 +128,42 @@ StringPrototype::StringPrototype(VM& vm, Structure* structure)
 void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage)
 {
     Base::finishCreation(vm, nameAndMessage);
-    ASSERT(inherits(info()));
-
-    JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
-    JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
-    JSC_NATIVE_INTRINSIC_FUNCTION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
-    JSC_NATIVE_INTRINSIC_FUNCTION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
-    JSC_NATIVE_FUNCTION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("concat", stringProtoFuncConcat, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("match", stringProtoFuncMatch, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("repeat", stringProtoFuncRepeat, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("replace", stringProtoFuncReplace, DontEnum, 2);
-    JSC_NATIVE_FUNCTION("search", stringProtoFuncSearch, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("slice", stringProtoFuncSlice, DontEnum, 2);
-    JSC_NATIVE_FUNCTION("split", stringProtoFuncSplit, DontEnum, 2);
-    JSC_NATIVE_FUNCTION("substr", stringProtoFuncSubstr, DontEnum, 2);
-    JSC_NATIVE_FUNCTION("substring", stringProtoFuncSubstring, DontEnum, 2);
-    JSC_NATIVE_FUNCTION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
+    ASSERT(inherits(vm, info()));
+
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->toString, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, stringProtoFuncToString, DontEnum, 0, StringPrototypeValueOfIntrinsic);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charAt", stringProtoFuncCharAt, DontEnum, 1, CharAtIntrinsic);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("charCodeAt", stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("codePointAt", stringProtoFuncCodePointAt, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("indexOf", stringProtoFuncIndexOf, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingRegExpPrivateName(), stringProtoFuncReplaceUsingRegExp, DontEnum, 2, StringPrototypeReplaceRegExpIntrinsic);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().replaceUsingStringSearchPrivateName(), stringProtoFuncReplaceUsingStringSearch, DontEnum, 2);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0, StringPrototypeToLowerCaseIntrinsic);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
 #if ENABLE(INTL)
-    JSC_NATIVE_FUNCTION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("toLocaleUpperCase", stringProtoFuncToLocaleUpperCase, DontEnum, 0);
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, DontEnum);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLocaleLowerCase, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToLocaleUpperCase, DontEnum, 0);
 #else
-    JSC_NATIVE_FUNCTION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringProtoFuncLocaleCompare, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
 #endif
-    JSC_NATIVE_FUNCTION("big", stringProtoFuncBig, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("small", stringProtoFuncSmall, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("blink", stringProtoFuncBlink, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("bold", stringProtoFuncBold, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("fixed", stringProtoFuncFixed, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("italics", stringProtoFuncItalics, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("strike", stringProtoFuncStrike, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("sub", stringProtoFuncSub, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("sup", stringProtoFuncSup, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("fontsize", stringProtoFuncFontsize, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("anchor", stringProtoFuncAnchor, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("link", stringProtoFuncLink, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("trim", stringProtoFuncTrim, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("trimRight", stringProtoFuncTrimRight, DontEnum, 0);
-    JSC_NATIVE_FUNCTION("startsWith", stringProtoFuncStartsWith, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("endsWith", stringProtoFuncEndsWith, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("includes", stringProtoFuncIncludes, DontEnum, 1);
-    JSC_NATIVE_FUNCTION("normalize", stringProtoFuncNormalize, DontEnum, 1);
-    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorSymbol, stringProtoFuncIterator, DontEnum, 0);
-
-    JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->charCodeAtPrivateName, stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trim", stringProtoFuncTrim, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimLeft", stringProtoFuncTrimLeft, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("trimRight", stringProtoFuncTrimRight, DontEnum, 0);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("startsWith", stringProtoFuncStartsWith, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("endsWith", stringProtoFuncEndsWith, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("includes", stringProtoFuncIncludes, DontEnum, 1);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("normalize", stringProtoFuncNormalize, DontEnum, 0);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().charCodeAtPrivateName(), stringProtoFuncCharCodeAt, DontEnum, 1, CharCodeAtIntrinsic);
+
+    JSFunction* iteratorFunction = JSFunction::create(vm, globalObject, 0, ASCIILiteral("[Symbol.iterator]"), stringProtoFuncIterator, NoIntrinsic);
+    putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, iteratorFunction, DontEnum);
 
     // The constructor will be added later, after StringConstructor has been built
     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
@@ -176,31 +179,10 @@ StringPrototype* StringPrototype::create(VM& vm, JSGlobalObject* globalObject, S
 
 // ------------------------------ Functions --------------------------
 
-// Helper for producing a JSString for 'string', where 'string' was been produced by
-// calling ToString on 'originalValue'. In cases where 'originalValue' already was a
-// string primitive we can just use this, otherwise we need to allocate a new JSString.
-static inline JSString* jsStringWithReuse(ExecState* exec, JSValue originalValue, const String& string)
-{
-    if (originalValue.isString()) {
-        ASSERT(asString(originalValue)->value(exec) == string);
-        return asString(originalValue);
-    }
-    return jsString(exec, string);
-}
-
-// Helper that tries to use the JSString substring sharing mechanism if 'originalValue' is a JSString.
-static inline JSString* jsSubstring(ExecState* exec, JSValue originalValue, const String& string, unsigned offset, unsigned length)
-{
-    if (originalValue.isString()) {
-        ASSERT(asString(originalValue)->value(exec) == string);
-        return jsSubstring(exec, asString(originalValue), offset, length);
-    }
-    return jsSubstring(exec, string, offset, length);
-}
-
 static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i)
 {
     StringBuilder substitutedReplacement;
+    bool hasNamedCaptures = reg && reg->hasNamedCaptures();
     int offset = 0;
     do {
         if (i + 1 == replacement.length())
@@ -227,14 +209,48 @@ static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement,
         } else if (ref == '\'') {
             backrefStart = ovector[1];
             backrefLength = source.length() - backrefStart;
-        } else if (reg && ref >= '0' && ref <= '9') {
+        } else if (reg && ref == '<') {
+            // Named back reference
+            if (!hasNamedCaptures) {
+                substitutedReplacement.append(replacement.substring(i, 2));
+                offset = i + 2;
+                advance = 1;
+                continue;
+            }
+
+            size_t closingBracket = replacement.find('>', i + 2);
+            if (closingBracket == WTF::notFound) {
+                // FIXME: https://bugs.webkit.org/show_bug.cgi?id=176434
+                // Current proposed spec change throws a syntax error in this case.
+                // We have made the case that it makes more sense to treat this a literal
+                // If throwSyntaxError(exec, scope, "Missing closing '>' in replacement text");
+                continue;
+            }
+
+            unsigned nameLength = closingBracket - i - 2;
+            unsigned backrefIndex = reg->subpatternForName(replacement.substring(i + 2, nameLength).toString());
+
+            if (!backrefIndex || backrefIndex > reg->numSubpatterns()) {
+                // FIXME: https://bugs.webkit.org/show_bug.cgi?id=176434
+                // Proposed spec change throws a throw syntax error in this case.
+                // We have made the case that a non-existent back reference should be replaced with
+                // and empty string.
+                // throwSyntaxError(exec, scope, makeString("Replacement text references non-existent backreference \"" + replacement.substring(i + 2, nameLength).toString()));
+                backrefStart = 0;
+                backrefLength = 0;
+            } else {
+                backrefStart = ovector[2 * backrefIndex];
+                backrefLength = ovector[2 * backrefIndex + 1] - backrefStart;
+            }
+            advance = nameLength + 1;
+        } else if (reg && isASCIIDigit(ref)) {
             // 1- and 2-digit back references are allowed
             unsigned backrefIndex = ref - '0';
             if (backrefIndex > reg->numSubpatterns())
                 continue;
             if (replacement.length() > i + 2) {
                 ref = replacement[i + 2];
-                if (ref >= '0' && ref <= '9') {
+                if (isASCIIDigit(ref)) {
                     backrefIndex = 10 * backrefIndex + ref - '0';
                     if (backrefIndex > reg->numSubpatterns())
                         backrefIndex = backrefIndex / 10;   // Fall back to the 1-digit reference
@@ -263,7 +279,7 @@ static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement,
     return substitutedReplacement.toString();
 }
 
-static inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
+inline String substituteBackreferencesInline(const String& replacement, StringView source, const int* ovector, RegExp* reg)
 {
     size_t i = replacement.find('$');
     if (UNLIKELY(i != notFound))
@@ -272,6 +288,11 @@ static inline String substituteBackreferences(const String& replacement, StringV
     return replacement;
 }
 
+String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg)
+{
+    return substituteBackreferencesInline(replacement, source, ovector, reg);
+}
+
 struct StringRange {
     StringRange(int pos, int len)
         : position(pos)
@@ -289,6 +310,9 @@ struct StringRange {
 
 static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     if (rangeCount == 1) {
         int sourceSize = source.length();
         int position = substringRanges[0].position;
@@ -296,7 +320,8 @@ static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourc
         if (position <= 0 && length >= sourceSize)
             return sourceVal;
         // We could call String::substringSharingImpl(), but this would result in redundant checks.
-        return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
+        scope.release();
+        return jsString(exec, StringImpl::createSubstringSharingImpl(*source.impl(), std::max(0, position), std::min(sourceSize, length)));
     }
 
     int totalLength = 0;
@@ -309,9 +334,9 @@ static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourc
     if (source.is8Bit()) {
         LChar* buffer;
         const LChar* sourceData = source.characters8();
-        RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
+        auto impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
         if (!impl)
-            return throwOutOfMemoryError(exec);
+            return throwOutOfMemoryError(exec, scope);
 
         int bufferPos = 0;
         for (int i = 0; i < rangeCount; i++) {
@@ -321,15 +346,16 @@ static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourc
             }
         }
 
-        return jsString(exec, impl.release());
+        scope.release();
+        return jsString(exec, WTFMove(impl));
     }
 
     UChar* buffer;
     const UChar* sourceData = source.characters16();
 
-    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
+    auto impl = StringImpl::tryCreateUninitialized(totalLength, buffer);
     if (!impl)
-        return throwOutOfMemoryError(exec);
+        return throwOutOfMemoryError(exec, scope);
 
     int bufferPos = 0;
     for (int i = 0; i < rangeCount; i++) {
@@ -339,11 +365,15 @@ static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourc
         }
     }
 
-    return jsString(exec, impl.release());
+    scope.release();
+    return jsString(exec, WTFMove(impl));
 }
 
 static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     if (rangeCount == 1 && separatorCount == 0) {
         int sourceSize = source.length();
         int position = substringRanges[0].position;
@@ -351,7 +381,8 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
         if (position <= 0 && length >= sourceSize)
             return sourceVal;
         // We could call String::substringSharingImpl(), but this would result in redundant checks.
-        return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length)));
+        scope.release();
+        return jsString(exec, StringImpl::createSubstringSharingImpl(*source.impl(), std::max(0, position), std::min(sourceSize, length)));
     }
 
     Checked<int, RecordOverflow> totalLength = 0;
@@ -364,7 +395,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
             allSeparators8Bit = false;
     }
     if (totalLength.hasOverflowed())
-        return throwOutOfMemoryError(exec);
+        return throwOutOfMemoryError(exec, scope);
 
     if (!totalLength)
         return jsEmptyString(exec);
@@ -373,9 +404,9 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
         LChar* buffer;
         const LChar* sourceData = source.characters8();
 
-        RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
+        auto impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
         if (!impl)
-            return throwOutOfMemoryError(exec);
+            return throwOutOfMemoryError(exec, scope);
 
         int maxCount = std::max(rangeCount, separatorCount);
         int bufferPos = 0;
@@ -394,13 +425,14 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
             }
         }        
 
-        return jsString(exec, impl.release());
+        scope.release();
+        return jsString(exec, WTFMove(impl));
     }
 
     UChar* buffer;
-    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
+    auto impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer);
     if (!impl)
-        return throwOutOfMemoryError(exec);
+        return throwOutOfMemoryError(exec, scope);
 
     int maxCount = std::max(rangeCount, separatorCount);
     int bufferPos = 0;
@@ -425,27 +457,37 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
         }
     }
 
-    return jsString(exec, impl.release());
+    scope.release();
+    return jsString(exec, WTFMove(impl));
 }
 
-static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp)
+#define OUT_OF_MEMORY(exec__, scope__) \
+    do { \
+        throwOutOfMemoryError(exec__, scope__); \
+        return encodedJSValue(); \
+    } while (false)
+
+static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, const String& source, RegExp* regExp)
 {
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    SuperSamplerScope superSamplerScope(false);
+    
     size_t lastIndex = 0;
     unsigned startPosition = 0;
 
     Vector<StringRange, 16> sourceRanges;
-    VM* vm = &exec->vm();
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
     unsigned sourceLen = source.length();
 
     while (true) {
-        MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition);
+        MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition);
         if (!result)
             break;
 
-        if (lastIndex < result.start)
-            sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
-
+        if (lastIndex < result.start) {
+            if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
+                OUT_OF_MEMORY(exec, scope);
+        }
         lastIndex = result.end;
         startPosition = lastIndex;
 
@@ -460,39 +502,41 @@ static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSSt
     if (!lastIndex)
         return JSValue::encode(string);
 
-    if (static_cast<unsigned>(lastIndex) < sourceLen)
-        sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
-
+    if (static_cast<unsigned>(lastIndex) < sourceLen) {
+        if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
+            OUT_OF_MEMORY(exec, scope);
+    }
+    scope.release();
     return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size()));
 }
 
-static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue)
+static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
+    VM& vm, ExecState* exec, JSString* string, JSValue searchValue, CallData& callData,
+    CallType callType, String& replacementString, JSValue replaceValue)
 {
-    JSValue replaceValue = exec->argument(1);
-    String replacementString;
-    CallData callData;
-    CallType callType = getCallData(replaceValue, callData);
-    if (callType == CallTypeNone)
-        replacementString = replaceValue.toString(exec)->value(exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
     const String& source = string->value(exec);
     unsigned sourceLen = source.length();
-    if (exec->hadException())
-        return JSValue::encode(JSValue());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     RegExpObject* regExpObject = asRegExpObject(searchValue);
     RegExp* regExp = regExpObject->regExp();
     bool global = regExp->global();
+    bool hasNamedCaptures = regExp->hasNamedCaptures();
 
     if (global) {
         // ES5.1 15.5.4.10 step 8.a.
         regExpObject->setLastIndex(exec, 0);
-        if (exec->hadException())
-            return JSValue::encode(JSValue());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-        if (callType == CallTypeNone && !replacementString.length())
-            return removeUsingRegExpSearch(exec, string, source, regExp);
+        if (callType == CallType::None && !replacementString.length()) {
+            scope.release();
+            return removeUsingRegExpSearch(vm, exec, string, source, regExp);
+        }
     }
 
+    // FIXME: This is wrong because we may be called directly from the FTL.
+    // https://bugs.webkit.org/show_bug.cgi?id=154874
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
 
     size_t lastIndex = 0;
@@ -502,42 +546,62 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
     Vector<String, 16> replacements;
 
     // This is either a loop (if global is set) or a one-way (if not).
-    if (global && callType == CallTypeJS) {
+    if (global && callType == CallType::JS) {
         // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string
         int argCount = regExp->numSubpatterns() + 1 + 2;
         JSFunction* func = jsCast<JSFunction*>(replaceValue);
         CachedCall cachedCall(exec, func, argCount);
-        if (exec->hadException())
-            return JSValue::encode(jsNull());
-        VM* vm = &exec->vm();
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
         if (source.is8Bit()) {
             while (true) {
                 int* ovector;
-                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
+                MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
                 if (!result)
                     break;
 
-                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
+                if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
+                    OUT_OF_MEMORY(exec, scope);
 
                 unsigned i = 0;
+                cachedCall.clearArguments();
+
+                JSObject* groups = nullptr;
+
+                if (hasNamedCaptures) {
+                    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+                    groups = JSFinalObject::create(vm, JSFinalObject::createStructure(vm, globalObject, globalObject->objectPrototype(), 0));
+                }
+
                 for (; i < regExp->numSubpatterns() + 1; ++i) {
                     int matchStart = ovector[i * 2];
                     int matchLen = ovector[i * 2 + 1] - matchStart;
 
+                    JSValue patternValue;
+
                     if (matchStart < 0)
-                        cachedCall.setArgument(i, jsUndefined());
+                        patternValue = jsUndefined();
                     else
-                        cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen));
+                        patternValue = jsSubstring(&vm, source, matchStart, matchLen);
+
+                    cachedCall.appendArgument(patternValue);
+
+                    if (i && hasNamedCaptures) {
+                        String groupName = regExp->getCaptureGroupName(i);
+                        if (!groupName.isEmpty())
+                            groups->putDirect(vm, Identifier::fromString(&vm, groupName), patternValue);
+                    }
                 }
 
-                cachedCall.setArgument(i++, jsNumber(result.start));
-                cachedCall.setArgument(i++, string);
+                cachedCall.appendArgument(jsNumber(result.start));
+                cachedCall.appendArgument(string);
+                if (hasNamedCaptures)
+                    cachedCall.appendArgument(groups);
 
                 cachedCall.setThis(jsUndefined());
                 JSValue jsResult = cachedCall.call();
-                replacements.append(jsResult.toString(exec)->value(exec));
-                if (exec->hadException())
-                    break;
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                replacements.append(jsResult.toWTFString(exec));
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
                 lastIndex = result.end;
                 startPosition = lastIndex;
@@ -552,31 +616,53 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
         } else {
             while (true) {
                 int* ovector;
-                MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
+                MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
                 if (!result)
                     break;
 
-                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
+                if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
+                    OUT_OF_MEMORY(exec, scope);
 
                 unsigned i = 0;
+                cachedCall.clearArguments();
+
+                JSObject* groups = nullptr;
+
+                if (hasNamedCaptures) {
+                    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+                    groups = JSFinalObject::create(vm, JSFinalObject::createStructure(vm, globalObject, globalObject->objectPrototype(), 0));
+                }
+
                 for (; i < regExp->numSubpatterns() + 1; ++i) {
                     int matchStart = ovector[i * 2];
                     int matchLen = ovector[i * 2 + 1] - matchStart;
 
+                    JSValue patternValue;
+
                     if (matchStart < 0)
-                        cachedCall.setArgument(i, jsUndefined());
+                        patternValue = jsUndefined();
                     else
-                        cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen));
+                        patternValue = jsSubstring(&vm, source, matchStart, matchLen);
+
+                    cachedCall.appendArgument(patternValue);
+
+                    if (i && hasNamedCaptures) {
+                        String groupName = regExp->getCaptureGroupName(i);
+                        if (!groupName.isEmpty())
+                            groups->putDirect(vm, Identifier::fromString(&vm, groupName), patternValue);
+                    }
                 }
 
-                cachedCall.setArgument(i++, jsNumber(result.start));
-                cachedCall.setArgument(i++, string);
+                cachedCall.appendArgument(jsNumber(result.start));
+                cachedCall.appendArgument(string);
+                if (hasNamedCaptures)
+                    cachedCall.appendArgument(groups);
 
                 cachedCall.setThis(jsUndefined());
                 JSValue jsResult = cachedCall.call();
-                replacements.append(jsResult.toString(exec)->value(exec));
-                if (exec->hadException())
-                    break;
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                replacements.append(jsResult.toWTFString(exec));
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
                 lastIndex = result.end;
                 startPosition = lastIndex;
@@ -590,38 +676,61 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
             }
         }
     } else {
-        VM* vm = &exec->vm();
         do {
             int* ovector;
-            MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector);
+            MatchResult result = regExpConstructor->performMatch(vm, regExp, string, source, startPosition, &ovector);
             if (!result)
                 break;
 
-            if (callType != CallTypeNone) {
-                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
+            if (callType != CallType::None) {
+                if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
+                    OUT_OF_MEMORY(exec, scope);
 
                 MarkedArgumentBuffer args;
+                JSObject* groups = nullptr;
+
+                if (hasNamedCaptures) {
+                    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+                    groups = JSFinalObject::create(vm, JSFinalObject::createStructure(vm, globalObject, globalObject->objectPrototype(), 0));
+                }
 
                 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
                     int matchStart = ovector[i * 2];
                     int matchLen = ovector[i * 2 + 1] - matchStart;
 
+                    JSValue patternValue;
+
                     if (matchStart < 0)
-                        args.append(jsUndefined());
+                        patternValue = jsUndefined();
                     else
-                        args.append(jsSubstring(exec, source, matchStart, matchLen));
+                        patternValue = jsSubstring(exec, source, matchStart, matchLen);
+
+                    args.append(patternValue);
+
+                    if (i && hasNamedCaptures) {
+                        String groupName = regExp->getCaptureGroupName(i);
+                        if (!groupName.isEmpty())
+                            groups->putDirect(vm, Identifier::fromString(&vm, groupName), patternValue);
+                    }
+
                 }
 
                 args.append(jsNumber(result.start));
                 args.append(string);
-
-                replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
-                if (exec->hadException())
-                    break;
+                if (hasNamedCaptures)
+                    args.append(groups);
+
+                JSValue replacement = call(exec, replaceValue, callType, callData, jsUndefined(), args);
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                String replacementString = replacement.toWTFString(exec);
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                replacements.append(replacementString);
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
             } else {
                 int replLen = replacementString.length();
                 if (lastIndex < result.start || replLen) {
-                    sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
+                    if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, result.start - lastIndex)))
+                        OUT_OF_MEMORY(exec, scope);
 
                     if (replLen)
                         replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
@@ -645,50 +754,104 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
     if (!lastIndex && replacements.isEmpty())
         return JSValue::encode(string);
 
-    if (static_cast<unsigned>(lastIndex) < sourceLen)
-        sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex));
-
+    if (static_cast<unsigned>(lastIndex) < sourceLen) {
+        if (UNLIKELY(!sourceRanges.tryConstructAndAppend(lastIndex, sourceLen - lastIndex)))
+            OUT_OF_MEMORY(exec, scope);
+    }
+    scope.release();
     return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()));
 }
 
-static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue)
+EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
+    ExecState* exec, JSString* thisValue, RegExpObject* searchValue)
 {
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    RegExp* regExp = searchValue->regExp();
+    if (regExp->global()) {
+        // ES5.1 15.5.4.10 step 8.a.
+        searchValue->setLastIndex(exec, 0);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        scope.release();
+        return removeUsingRegExpSearch(vm, exec, thisValue, thisValue->value(exec), regExp);
+    }
+
+    CallData callData;
+    String replacementString = emptyString();
+    scope.release();
+    return replaceUsingRegExpSearch(
+        vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, JSValue());
+}
+
+EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
+    ExecState* exec, JSString* thisValue, RegExpObject* searchValue, JSString* replaceString)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    
+    CallData callData;
+    String replacementString = replaceString->value(exec);
+    return replaceUsingRegExpSearch(
+        vm, exec, thisValue, searchValue, callData, CallType::None, replacementString, replaceString);
+}
+
+static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    String replacementString;
+    CallData callData;
+    CallType callType = getCallData(replaceValue, callData);
+    if (callType == CallType::None) {
+        replacementString = replaceValue.toWTFString(exec);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    }
+
+    scope.release();
+    return replaceUsingRegExpSearch(
+        vm, exec, string, searchValue, callData, callType, replacementString, replaceValue);
+}
+
+static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM& vm, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     const String& string = jsString->value(exec);
-    String searchString = searchValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    String searchString = searchValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     size_t matchStart = string.find(searchString);
 
     if (matchStart == notFound)
         return JSValue::encode(jsString);
 
-    JSValue replaceValue = exec->argument(1);
     CallData callData;
     CallType callType = getCallData(replaceValue, callData);
-    if (callType != CallTypeNone) {
+    if (callType != CallType::None) {
         MarkedArgumentBuffer args;
         args.append(jsSubstring(exec, string, matchStart, searchString.impl()->length()));
         args.append(jsNumber(matchStart));
         args.append(jsString);
         replaceValue = call(exec, replaceValue, callType, callData, jsUndefined(), args);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
 
-    String replaceString = replaceValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String replaceString = replaceValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     StringImpl* stringImpl = string.impl();
-    String leftPart(StringImpl::createSubstringSharingImpl(stringImpl, 0, matchStart));
+    String leftPart(StringImpl::createSubstringSharingImpl(*stringImpl, 0, matchStart));
 
     size_t matchEnd = matchStart + searchString.impl()->length();
     int ovector[2] = { static_cast<int>(matchStart),  static_cast<int>(matchEnd)};
-    String middlePart = substituteBackreferences(replaceString, string, ovector, 0);
+    String middlePart = callType != CallType::None ? replaceString : substituteBackreferences(replaceString, string, ovector, 0);
 
     size_t leftLength = stringImpl->length() - matchEnd;
-    String rightPart(StringImpl::createSubstringSharingImpl(stringImpl, matchEnd, leftLength));
+    String rightPart(StringImpl::createSubstringSharingImpl(*stringImpl, matchEnd, leftLength));
+    scope.release();
     return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
 }
 
@@ -700,134 +863,189 @@ static inline bool checkObjectCoercible(JSValue thisValue)
     if (thisValue.isUndefinedOrNull())
         return false;
 
-    if (thisValue.isCell() && thisValue.asCell()->structure()->typeInfo().isEnvironmentRecord())
+    if (thisValue.isObject() && asObject(thisValue)->isEnvironmentRecord())
         return false;
 
     return true;
 }
 
 template <typename CharacterType>
-static inline JSValue repeatCharacter(ExecState* exec, CharacterType character, unsigned repeatCount)
+static inline JSString* repeatCharacter(ExecState& exec, CharacterType character, unsigned repeatCount)
 {
+    VM& vm = exec.vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     CharacterType* buffer = nullptr;
-    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
-    if (!impl)
-        return throwOutOfMemoryError(exec);
+    auto impl = StringImpl::tryCreateUninitialized(repeatCount, buffer);
+    if (!impl) {
+        throwOutOfMemoryError(&exec, scope);
+        return nullptr;
+    }
 
     std::fill_n(buffer, repeatCount, character);
 
-    return jsString(exec, impl.release());
+    scope.release();
+    return jsString(&exec, WTFMove(impl));
 }
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeat(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
 {
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSString* string = thisValue.toString(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    // For a string which length is single, instead of creating ropes,
+    // allocating a sequential buffer and fill with the repeated string for efficiency.
+    ASSERT(exec->argumentCount() == 2);
+
+    ASSERT(exec->uncheckedArgument(0).isString());
+    JSString* string = asString(exec->uncheckedArgument(0));
+    ASSERT(string->length() == 1);
+
+    JSValue repeatCountValue = exec->uncheckedArgument(1);
+    RELEASE_ASSERT(repeatCountValue.isNumber());
+    int32_t repeatCount;
+    double value = repeatCountValue.asNumber();
+    if (value > JSString::MaxLength)
+        return JSValue::encode(throwOutOfMemoryError(exec, scope));
+    repeatCount = static_cast<int32_t>(value);
+    ASSERT(repeatCount >= 0);
+    ASSERT(!repeatCountValue.isDouble() || repeatCountValue.asDouble() == repeatCount);
+
+    auto viewWithString = string->viewWithUnderlyingString(exec);
+    StringView view = viewWithString.view;
+    ASSERT(view.length() == 1);
+    scope.assertNoException();
+    UChar character = view[0];
+    scope.release();
+    if (!(character & ~0xff))
+        return JSValue::encode(repeatCharacter(*exec, static_cast<LChar>(character), repeatCount));
+    return JSValue::encode(repeatCharacter(*exec, character, repeatCount));
+}
 
-    double repeatCountDouble = exec->argument(0).toInteger(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    if (repeatCountDouble < 0 || std::isinf(repeatCountDouble))
-        return throwVMError(exec, createRangeError(exec, ASCIILiteral("repeat() argument must be greater than or equal to 0 and not be infinity")));
+ALWAYS_INLINE EncodedJSValue replace(
+    VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
+{
+    if (searchValue.inherits(vm, RegExpObject::info()))
+        return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
+    return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
+}
 
-    VM& vm = exec->vm();
+ALWAYS_INLINE EncodedJSValue replace(
+    VM& vm, ExecState* exec, JSValue thisValue, JSValue searchValue, JSValue replaceValue)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
-    if (!string->length() || !repeatCountDouble)
-        return JSValue::encode(jsEmptyString(&vm));
+    if (!checkObjectCoercible(thisValue))
+        return throwVMTypeError(exec, scope);
+    JSString* string = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    scope.release();
+    return replace(vm, exec, string, searchValue, replaceValue);
+}
 
-    if (repeatCountDouble == 1)
-        return JSValue::encode(string);
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
-    // JSString requires the limitation that its length is in the range of int32_t.
-    if (repeatCountDouble > std::numeric_limits<int32_t>::max() / string->length())
-        return JSValue::encode(throwOutOfMemoryError(exec));
-    unsigned repeatCount = static_cast<unsigned>(repeatCountDouble);
+    JSString* string = exec->thisValue().toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    // For a string which length is small, instead of creating ropes,
-    // allocating a sequential buffer and fill with the repeated string for efficiency.
-    if (string->length() == 1) {
-        String repeatedString = string->value(exec);
-        UChar character = repeatedString.at(0);
-        if (!(character & ~0xff))
-            return JSValue::encode(repeatCharacter(exec, static_cast<LChar>(character), repeatCount));
-        return JSValue::encode(repeatCharacter(exec, character, repeatCount));
-    }
+    JSValue searchValue = exec->argument(0);
+    if (!searchValue.inherits(vm, RegExpObject::info()))
+        return JSValue::encode(jsUndefined());
 
-    JSRopeString::RopeBuilder ropeBuilder(vm);
-    for (unsigned i = 0; i < repeatCount; ++i) {
-        if (!ropeBuilder.append(string))
-            return JSValue::encode(throwOutOfMemoryError(exec));
-    }
-    return JSValue::encode(ropeBuilder.release());
+    scope.release();
+    return replaceUsingRegExpSearch(vm, exec, string, searchValue, exec->argument(1));
 }
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState* exec)
 {
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    JSString* string = thisValue.toString(exec);
-    JSValue searchValue = exec->argument(0);
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSString* string = exec->thisValue().toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    if (searchValue.inherits(RegExpObject::info()))
-        return replaceUsingRegExpSearch(exec, string, searchValue);
-    return replaceUsingStringSearch(exec, string, searchValue);
+    scope.release();
+    return replaceUsingStringSearch(vm, exec, string, exec->argument(0), exec->argument(1));
+}
+
+EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
+    ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
+    EncodedJSValue replaceValue)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    
+    return replace(
+        vm, exec, JSValue::decode(thisValue), JSValue::decode(searchValue),
+        JSValue::decode(replaceValue));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     // Also used for valueOf.
 
     if (thisValue.isString())
         return JSValue::encode(thisValue);
 
-    if (thisValue.inherits(StringObject::info()))
+    if (thisValue.inherits(vm, StringObject::info()))
         return JSValue::encode(asStringObject(thisValue)->internalValue());
 
-    return throwVMTypeError(exec);
+    return throwVMTypeError(exec, scope);
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    StringView string = thisValue.toString(exec)->view(exec);
+        return throwVMTypeError(exec, scope);
+    auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    StringView view = viewWithString.view;
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     JSValue a0 = exec->argument(0);
     if (a0.isUInt32()) {
         uint32_t i = a0.asUInt32();
-        if (i < string.length())
-            return JSValue::encode(jsSingleCharacterString(exec, string[i]));
+        if (i < view.length())
+            return JSValue::encode(jsSingleCharacterString(exec, view[i]));
         return JSValue::encode(jsEmptyString(exec));
     }
     double dpos = a0.toInteger(exec);
-    if (dpos >= 0 && dpos < string.length())
-        return JSValue::encode(jsSingleCharacterString(exec, string[static_cast<unsigned>(dpos)]));
+    if (dpos >= 0 && dpos < view.length())
+        return JSValue::encode(jsSingleCharacterString(exec, view[static_cast<unsigned>(dpos)]));
     return JSValue::encode(jsEmptyString(exec));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    StringView string = thisValue.toString(exec)->view(exec);
+        return throwVMTypeError(exec, scope);
+    auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    StringView view = viewWithString.view;
     JSValue a0 = exec->argument(0);
     if (a0.isUInt32()) {
         uint32_t i = a0.asUInt32();
-        if (i < string.length())
-            return JSValue::encode(jsNumber(string[i]));
+        if (i < view.length())
+            return JSValue::encode(jsNumber(view[i]));
         return JSValue::encode(jsNaN());
     }
     double dpos = a0.toInteger(exec);
-    if (dpos >= 0 && dpos < string.length())
-        return JSValue::encode(jsNumber(string[static_cast<int>(dpos)]));
+    if (dpos >= 0 && dpos < view.length())
+        return JSValue::encode(jsNumber(view[static_cast<int>(dpos)]));
     return JSValue::encode(jsNaN());
 }
 
@@ -843,11 +1061,15 @@ static inline UChar32 codePointAt(const String& string, unsigned position, unsig
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
     String string = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     unsigned length = string.length();
 
     JSValue argument0 = exec->argument(0);
@@ -858,37 +1080,31 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
         return JSValue::encode(jsUndefined());
     }
 
-    if (UNLIKELY(exec->hadException()))
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     double doublePosition = argument0.toInteger(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     if (doublePosition >= 0 && doublePosition < length)
         return JSValue::encode(jsNumber(codePointAt(string, static_cast<unsigned>(doublePosition), length)));
     return JSValue::encode(jsUndefined());
 }
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncConcat(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (thisValue.isString() && exec->argumentCount() == 1)
-        return JSValue::encode(jsString(exec, asString(thisValue), exec->uncheckedArgument(0).toString(exec)));
-
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    return JSValue::encode(jsStringFromArguments(exec, thisValue));
-}
-
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
     JSValue a0 = exec->argument(0);
     JSValue a1 = exec->argument(1);
 
     JSString* thisJSString = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     JSString* otherJSString = a0.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     unsigned pos = 0;
     if (!a1.isUndefined()) {
@@ -909,7 +1125,11 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
     if (thisJSString->length() < otherJSString->length() + pos)
         return JSValue::encode(jsNumber(-1));
 
-    size_t result = thisJSString->view(exec).get().find(otherJSString->view(exec), pos);
+    auto thisViewWithString = thisJSString->viewWithUnderlyingString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    auto otherViewWithString = otherJSString->viewWithUnderlyingString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    size_t result = thisViewWithString.view.find(otherViewWithString.view, pos);
     if (result == notFound)
         return JSValue::encode(jsNumber(-1));
     return JSValue::encode(jsNumber(result));
@@ -917,16 +1137,21 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
     JSValue a0 = exec->argument(0);
     JSValue a1 = exec->argument(1);
 
     JSString* thisJSString = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     unsigned len = thisJSString->length();
     JSString* otherJSString = a0.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     double dpos = a1.toIntegerPreserveNaN(exec);
     unsigned startPosition;
@@ -952,101 +1177,17 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
     return JSValue::encode(jsNumber(result));
 }
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
 {
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    JSString* string = thisValue.toString(exec);
-    String s = string->value(exec);
-    VM* vm = &exec->vm();
-
-    JSValue a0 = exec->argument(0);
-
-    RegExp* regExp;
-    bool global = false;
-    if (a0.inherits(RegExpObject::info())) {
-        RegExpObject* regExpObject = asRegExpObject(a0);
-        regExp = regExpObject->regExp();
-        if ((global = regExp->global())) {
-            // ES5.1 15.5.4.10 step 8.a.
-            regExpObject->setLastIndex(exec, 0);
-            if (exec->hadException())
-                return JSValue::encode(JSValue());
-        }
-    } else {
-        /*
-         *  ECMA 15.5.4.12 String.prototype.search (regexp)
-         *  If regexp is not an object whose [[Class]] property is "RegExp", it is
-         *  replaced with the result of the expression new RegExp(regexp).
-         *  Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
-         */
-        regExp = RegExp::create(exec->vm(), a0.isUndefined() ? emptyString() : a0.toString(exec)->value(exec), NoFlags);
-        if (!regExp->isValid())
-            return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage()));
-    }
-    RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
-    MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, s, 0);
-    // case without 'g' flag is handled like RegExp.prototype.exec
-    if (!global)
-        return JSValue::encode(result ? createRegExpMatchesArray(exec, string, regExp, result) : jsNull());
-
-    // return array of matches
-    MarkedArgumentBuffer list;
-    while (result) {
-        size_t end = result.end;
-        size_t length = end - result.start;
-        list.append(jsSubstring(exec, s, result.start, length));
-        if (!length)
-            ++end;
-        result = regExpConstructor->performMatch(*vm, regExp, string, s, end);
-    }
-    if (list.isEmpty()) {
-        // if there are no matches at all, it's important to return
-        // Null instead of an empty array, because this matches
-        // other browsers and because Null is a false value.
-        return JSValue::encode(jsNull());
-    }
-
-    return JSValue::encode(constructArray(exec, static_cast<ArrayAllocationProfile*>(0), list));
-}
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec)
-{
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    JSString* string = thisValue.toString(exec);
-    String s = string->value(exec);
-    VM* vm = &exec->vm();
+        return throwVMTypeError(exec, scope);
+    String s = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    JSValue a0 = exec->argument(0);
-
-    RegExp* reg;
-    if (a0.inherits(RegExpObject::info()))
-        reg = asRegExpObject(a0)->regExp();
-    else { 
-        /*
-         *  ECMA 15.5.4.12 String.prototype.search (regexp)
-         *  If regexp is not an object whose [[Class]] property is "RegExp", it is
-         *  replaced with the result of the expression new RegExp(regexp).
-         *  Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
-         */
-        reg = RegExp::create(exec->vm(), a0.isUndefined() ? emptyString() : a0.toString(exec)->value(exec), NoFlags);
-        if (!reg->isValid())
-            return throwVMError(exec, createSyntaxError(exec, reg->errorMessage()));
-    }
-    RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
-    MatchResult result = regExpConstructor->performMatch(*vm, reg, string, s, 0);
-    return JSValue::encode(result ? jsNumber(result.start) : jsNumber(-1));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
     int len = s.length();
     RELEASE_ASSERT(len >= 0);
 
@@ -1073,6 +1214,9 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
 template<typename CharacterType>
 static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray* result, JSValue originalValue, const String& input, StringImpl* string, UChar separatorCharacter, size_t& position, unsigned& resultLength, unsigned limitLength)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     // 12. Let q = p.
     size_t matchPosition;
     const CharacterType* characters = string->characters<CharacterType>();
@@ -1086,6 +1230,7 @@ static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray
         // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
         //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
         result->putDirectIndex(exec, resultLength, jsSubstring(exec, originalValue, input, position, matchPosition - position));
+        RETURN_IF_EXCEPTION(scope, false);
         // 3. Increment lengthA by 1.
         // 4. If lengthA == lim, return A.
         if (++resultLength == limitLength)
@@ -1098,240 +1243,159 @@ static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray
     return false;
 }
 
-// ES 5.1 - 15.5.4.14 String.prototype.split (separator, limit)
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSplit(ExecState* exec)
+// ES 21.1.3.17 String.prototype.split(separator, limit)
+EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
 {
-    // 1. Call CheckObjectCoercible passing the this value as its argument.
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+    ASSERT(checkObjectCoercible(thisValue));
 
-    // 2. Let S be the result of calling ToString, giving it the this value as its argument.
-    // 6. Let s be the number of characters in S.
-    String input = thisValue.toString(exec)->value(exec);
+    // 3. Let S be the result of calling ToString, giving it the this value as its argument.
+    // 7. Let s be the number of characters in S.
+    String input = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    ASSERT(!input.isNull());
 
-    // 3. Let A be a new array created as if by the expression new Array()
+    // 4. Let A be a new array created as if by the expression new Array()
     //    where Array is the standard built-in constructor with that name.
     JSArray* result = constructEmptyArray(exec, 0);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    // 4. Let lengthA be 0.
+    // 5. Let lengthA be 0.
     unsigned resultLength = 0;
 
-    // 5. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
-    JSValue limitValue = exec->argument(1);
+    // 6. If limit is undefined, let lim = 2^32-1; else let lim = ToUint32(limit).
+    JSValue limitValue = exec->uncheckedArgument(1);
     unsigned limit = limitValue.isUndefined() ? 0xFFFFFFFFu : limitValue.toUInt32(exec);
 
-    // 7. Let p = 0.
+    // 8. Let p = 0.
     size_t position = 0;
 
-    // 8. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
+    // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
     //    otherwise let R = ToString(separator).
-    JSValue separatorValue = exec->argument(0);
-    if (separatorValue.inherits(RegExpObject::info())) {
-        VM* vm = &exec->vm();
-        RegExp* reg = asRegExpObject(separatorValue)->regExp();
-
-        // 9. If lim == 0, return A.
-        if (!limit)
-            return JSValue::encode(result);
-
-        // 10. If separator is undefined, then
-        if (separatorValue.isUndefined()) {
-            // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
-            //    Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
-            result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
-            // b. Return A.
-            return JSValue::encode(result);
-        }
+    JSValue separatorValue = exec->uncheckedArgument(0);
+    String separator = separatorValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    // 10. If lim == 0, return A.
+    if (!limit)
+        return JSValue::encode(result);
+
+    // 11. If separator is undefined, then
+    if (separatorValue.isUndefined()) {
+        // a. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
+        scope.release();
+        result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
+        // b. Return A.
+        return JSValue::encode(result);
+    }
 
-        // 11. If s == 0, then
-        if (input.isEmpty()) {
-            // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
-            // b. If z is not failure, return A.
-            // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
-            //    Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
-            // d. Return A.
-            if (!reg->match(*vm, input, 0))
-                result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
-            return JSValue::encode(result);
+    // 12. If s == 0, then
+    if (input.isEmpty()) {
+        // a. Let z be SplitMatch(S, 0, R) where S is input, R is separator.
+        // b. If z is not false, return A.
+        // c. Call CreateDataProperty(A, "0", S).
+        // d. Return A.
+        if (!separator.isEmpty()) {
+            scope.release();
+            result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
         }
+        return JSValue::encode(result);
+    }
 
-        // 12. Let q = p.
-        size_t matchPosition = 0;
-        // 13. Repeat, while q != s
-        while (matchPosition < input.length()) {
-            // a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
-            Vector<int, 32> ovector;
-            int mpos = reg->match(*vm, input, matchPosition, ovector);
+    // Optimized case for splitting on the empty string.
+    if (separator.isEmpty()) {
+        limit = std::min(limit, input.length());
+        // Zero limt/input length handled in steps 9/11 respectively, above.
+        ASSERT(limit);
 
-            // b. If z is a failure then we can break because there are no matches
-            if (mpos < 0)
-                break;
-            matchPosition = mpos;
+        do {
+            result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
+            RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        } while (++position < limit);
 
-            // if the match is the empty match at the end, break.
-            if (matchPosition >= input.length())
-                break;
+        return JSValue::encode(result);
+    }
 
-            // c. Else, z is not failure
-            // i. z must be a State. Let e be z's endIndex and let cap be z's captures array.
-            size_t matchEnd = ovector[1];
+    // 3 cases:
+    // -separator length == 1, 8 bits
+    // -separator length == 1, 16 bits
+    // -separator length > 1
+    StringImpl* stringImpl = input.impl();
+    StringImpl* separatorImpl = separator.impl();
+    size_t separatorLength = separatorImpl->length();
+
+    if (separatorLength == 1) {
+        UChar separatorCharacter;
+        if (separatorImpl->is8Bit())
+            separatorCharacter = separatorImpl->characters8()[0];
+        else
+            separatorCharacter = separatorImpl->characters16()[0];
 
-            // ii. If e == p, then let q = q + 1.
-            if (matchEnd == position) {
-                ++matchPosition;
-                continue;
+        if (stringImpl->is8Bit()) {
+            if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
+                scope.release();
+                return JSValue::encode(result);
             }
-            // iii. if matchEnd == 0 then position should also be zero and thus matchEnd should equal position.
-            ASSERT(matchEnd);
-
-            // iii. Else, e != p
-
+        } else {
+            if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
+                scope.release();
+                return JSValue::encode(result);
+            }
+        }
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    } else {
+        // 13. Let q = p.
+        size_t matchPosition;
+        // 14. Repeat, while q != s
+        //   a. let e be SplitMatch(S, q, R).
+        //   b. If e is failure, then let q = q+1.
+        //   c. Else, e is an integer index <= s.
+        while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
             // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
             //    through q (exclusive).
-            // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
-            //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+            // 2. Call CreateDataProperty(A, ToString(lengthA), T).
             result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
-
+            RETURN_IF_EXCEPTION(scope, encodedJSValue());
             // 3. Increment lengthA by 1.
             // 4. If lengthA == lim, return A.
             if (++resultLength == limit)
                 return JSValue::encode(result);
 
             // 5. Let p = e.
-            // 8. Let q = p.
-            position = matchEnd;
-            matchPosition = matchEnd;
-
-            // 6. Let i = 0.
-            // 7. Repeat, while i is not equal to the number of elements in cap.
-            //  a Let i = i + 1.
-            for (unsigned i = 1; i <= reg->numSubpatterns(); ++i) {
-                // b Call the [[DefineOwnProperty]] internal method of A with arguments
-                //   ToString(lengthA), Property Descriptor {[[Value]]: cap[i], [[Writable]]:
-                //   true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
-                int sub = ovector[i * 2];
-                result->putDirectIndex(exec, resultLength, sub < 0 ? jsUndefined() : jsSubstring(exec, thisValue, input, sub, ovector[i * 2 + 1] - sub));
-                // c Increment lengthA by 1.
-                // d If lengthA == lim, return A.
-                if (++resultLength == limit)
-                    return JSValue::encode(result);
-            }
-        }
-    } else {
-        String separator = separatorValue.toString(exec)->value(exec);
-
-        // 9. If lim == 0, return A.
-        if (!limit)
-            return JSValue::encode(result);
-
-        // 10. If separator is undefined, then
-        JSValue separatorValue = exec->argument(0);
-        if (separatorValue.isUndefined()) {
-            // a.  Call the [[DefineOwnProperty]] internal method of A with arguments "0",
-            //     Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
-            result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
-            // b.  Return A.
-            return JSValue::encode(result);
-        }
-
-        // 11. If s == 0, then
-        if (input.isEmpty()) {
-            // a. Call SplitMatch(S, 0, R) and let z be its MatchResult result.
-            // b. If z is not failure, return A.
-            // c. Call the [[DefineOwnProperty]] internal method of A with arguments "0",
-            //    Property Descriptor {[[Value]]: S, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
-            // d. Return A.
-            if (!separator.isEmpty())
-                result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
-            return JSValue::encode(result);
-        }
-
-        // Optimized case for splitting on the empty string.
-        if (separator.isEmpty()) {
-            limit = std::min(limit, input.length());
-            // Zero limt/input length handled in steps 9/11 respectively, above.
-            ASSERT(limit);
-
-            do {
-                result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
-            } while (++position < limit);
-
-            return JSValue::encode(result);
-        }
-
-        // 3 cases:
-        // -separator length == 1, 8 bits
-        // -separator length == 1, 16 bits
-        // -separator length > 1
-        StringImpl* stringImpl = input.impl();
-        StringImpl* separatorImpl = separator.impl();
-        size_t separatorLength = separatorImpl->length();
-
-        if (separatorLength == 1) {
-            UChar separatorCharacter;
-            if (separatorImpl->is8Bit())
-                separatorCharacter = separatorImpl->characters8()[0];
-            else
-                separatorCharacter = separatorImpl->characters16()[0];
-
-            if (stringImpl->is8Bit()) {
-                if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
-                    return JSValue::encode(result);
-            } else {
-                if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
-                    return JSValue::encode(result);
-            }
-        } else {
-            // 12. Let q = p.
-            size_t matchPosition;
-            // 13. Repeat, while q != s
-            //   a. Call SplitMatch(S, q, R) and let z be its MatchResult result.
-            //   b. If z is failure, then let q = q+1.
-            //   c. Else, z is not failure
-            while ((matchPosition = stringImpl->find(separatorImpl, position)) != notFound) {
-                // 1. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
-                //    through q (exclusive).
-                // 2. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA),
-                //    Property Descriptor {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
-                result->putDirectIndex(exec, resultLength, jsSubstring(exec, thisValue, input, position, matchPosition - position));
-                // 3. Increment lengthA by 1.
-                // 4. If lengthA == lim, return A.
-                if (++resultLength == limit)
-                    return JSValue::encode(result);
-
-                // 5. Let p = e.
-                // 8. Let q = p.
-                position = matchPosition + separator.length();
-            }
+            // 6. Let q = p.
+            position = matchPosition + separator.length();
         }
     }
 
-    // 14. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
+    // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
     //     through s (exclusive).
-    // 15. Call the [[DefineOwnProperty]] internal method of A with arguments ToString(lengthA), Property Descriptor
-    //     {[[Value]]: T, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
+    // 16. Call CreateDataProperty(A, ToString(lengthA), T).
+    scope.release();
     result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
 
-    // 16. Return A.
+    // 17. Return A.
     return JSValue::encode(result);
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
     unsigned len;
     JSString* jsString = 0;
     String uString;
     if (thisValue.isString()) {
-        jsString = jsCast<JSString*>(thisValue.asCell());
+        jsString = asString(thisValue);
         len = jsString->length();
     } else {
-        uString = thisValue.toString(exec)->value(exec);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        uString = thisValue.toWTFString(exec);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
         len = uString.length();
     }
 
@@ -1356,16 +1420,31 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
     return JSValue::encode(jsSubstring(exec, uString, substringStart, substringLength));
 }
 
+EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
+{
+    // @substrInternal should not have any observable side effects (e.g. it should not call
+    // GetMethod(..., @@toPrimitive) on the thisValue).
+
+    // It is ok to use the default stringProtoFuncSubstr as the implementation of
+    // @substrInternal because @substrInternal will only be called by builtins, which will
+    // guarantee that we only pass it a string thisValue. As a result, stringProtoFuncSubstr
+    // will not need to call toString() on the thisValue, and there will be no observable
+    // side-effects.
+    ASSERT(exec->thisValue().isString());
+    return stringProtoFuncSubstr(exec);
+}
+
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
 {
-    SamplingRegion samplingRegion("Doing substringing");
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
     JSString* jsString = thisValue.toString(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->argument(0);
     JSValue a1 = exec->argument(1);
@@ -1373,6 +1452,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
     RELEASE_ASSERT(len >= 0);
 
     double start = a0.toNumber(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     double end;
     if (!(start >= 0)) // check for negative values or NaN
         start = 0;
@@ -1382,6 +1462,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
         end = len;
     else { 
         end = a1.toNumber(exec);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
         if (!(end >= 0)) // check for negative values or NaN
             end = 0;
         else if (end > len)
@@ -1399,69 +1480,75 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
     JSString* sVal = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     const String& s = sVal->value(exec);
-
-    int sSize = s.length();
-    if (!sSize)
-        return JSValue::encode(sVal);
-    RELEASE_ASSERT(sSize >= 0);
-
-    StringImpl* ourImpl = s.impl();
-    RefPtr<StringImpl> lower = ourImpl->lower();
-    if (ourImpl == lower)
+    String lowercasedString = s.convertToLowercaseWithoutLocale();
+    if (lowercasedString.impl() == s.impl())
         return JSValue::encode(sVal);
-    return JSValue::encode(jsString(exec, String(lower.release())));
+    scope.release();
+    return JSValue::encode(jsString(exec, lowercasedString));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
     JSString* sVal = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     const String& s = sVal->value(exec);
-
-    int sSize = s.length();
-    if (!sSize)
+    String uppercasedString = s.convertToUppercaseWithoutLocale();
+    if (uppercasedString.impl() == s.impl())
         return JSValue::encode(sVal);
-
-    StringImpl* sImpl = s.impl();
-    RefPtr<StringImpl> upper = sImpl->upper();
-    if (sImpl == upper)
-        return JSValue::encode(sVal);
-    return JSValue::encode(jsString(exec, String(upper.release())));
+    scope.release();
+    return JSValue::encode(jsString(exec, uppercasedString));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
+        return throwVMTypeError(exec, scope);
+    String s = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->argument(0);
-    return JSValue::encode(jsNumber(Collator().collate(s, a0.toString(exec)->value(exec))));
+    String str = a0.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    return JSValue::encode(jsNumber(Collator().collate(s, str)));
 }
 
 #if ENABLE(INTL)
 static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UChar*, int32_t, const UChar*, int32_t, const char*, UErrorCode*))
 {
+    VM& vm = state->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     // 1. Let O be RequireObjectCoercible(this value).
     JSValue thisValue = state->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(state);
+        return throwVMTypeError(state, scope);
 
     // 2. Let S be ToString(O).
     JSString* sVal = thisValue.toString(state);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     const String& s = sVal->value(state);
 
     // 3. ReturnIfAbrupt(S).
-    if (state->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     // Optimization for empty strings.
     if (s.isEmpty())
@@ -1471,8 +1558,7 @@ static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UCha
     Vector<String> requestedLocales = canonicalizeLocaleList(*state, state->argument(0));
 
     // 5. ReturnIfAbrupt(requestedLocales).
-    if (state->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     // 6. Let len be the number of elements in requestedLocales.
     size_t len = requestedLocales.size();
@@ -1481,7 +1567,7 @@ static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UCha
     // a. Let requestedLocale be the first element of requestedLocales.
     // 8. Else
     // a. Let requestedLocale be DefaultLocale().
-    String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale();
+    String requestedLocale = len > 0 ? requestedLocales.first() : defaultLocale(*state);
 
     // 9. Let noExtensionsLocale be the String value that is requestedLocale with all Unicode locale extension sequences (6.2.1) removed.
     String noExtensionsLocale = removeUnicodeLocaleExtension(requestedLocale);
@@ -1521,12 +1607,13 @@ static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UCha
         Vector<UChar> buffer(resultLength);
         convertCase(buffer.data(), resultLength, view.upconvertedCharacters(), viewLength, utf8LocaleBuffer.data(), &error);
         if (U_FAILURE(error))
-            return throwVMTypeError(state, u_errorName(error));
+            return throwVMTypeError(state, scope, u_errorName(error));
         lower = String(buffer.data(), resultLength);
     } else
-        return throwVMTypeError(state, u_errorName(error));
+        return throwVMTypeError(state, scope, u_errorName(error));
 
     // 18. Return L.
+    scope.release();
     return JSValue::encode(jsString(state, lower));
 }
 
@@ -1546,200 +1633,6 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
 }
 #endif // ENABLE(INTL)
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    JSValue a0 = exec->argument(0);
-    String color = a0.toWTFString(exec);
-    color.replaceWithLiteral('"', "&quot;");
-
-    return JSValue::encode(jsMakeNontrivialString(exec, "<font color=\"", color, "\">", s, "</font>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFontsize(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    JSValue a0 = exec->argument(0);
-
-    uint32_t smallInteger;
-    if (a0.getUInt32(smallInteger) && smallInteger <= 9) {
-        unsigned stringSize = s.length();
-        unsigned bufferSize = 22 + stringSize;
-        // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
-        UChar* buffer;
-        PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
-        if (!impl)
-            return JSValue::encode(jsUndefined());
-        buffer[0] = '<';
-        buffer[1] = 'f';
-        buffer[2] = 'o';
-        buffer[3] = 'n';
-        buffer[4] = 't';
-        buffer[5] = ' ';
-        buffer[6] = 's';
-        buffer[7] = 'i';
-        buffer[8] = 'z';
-        buffer[9] = 'e';
-        buffer[10] = '=';
-        buffer[11] = '"';
-        buffer[12] = '0' + smallInteger;
-        buffer[13] = '"';
-        buffer[14] = '>';
-        StringView(s).getCharactersWithUpconvert(&buffer[15]);
-        buffer[15 + stringSize] = '<';
-        buffer[16 + stringSize] = '/';
-        buffer[17 + stringSize] = 'f';
-        buffer[18 + stringSize] = 'o';
-        buffer[19 + stringSize] = 'n';
-        buffer[20 + stringSize] = 't';
-        buffer[21 + stringSize] = '>';
-        return JSValue::encode(jsNontrivialString(exec, impl));
-    }
-
-    String fontSize = a0.toWTFString(exec);
-    fontSize.replaceWithLiteral('"', "&quot;");
-
-    return JSValue::encode(jsMakeNontrivialString(exec, "<font size=\"", fontSize, "\">", s, "</font>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncAnchor(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    JSValue a0 = exec->argument(0);
-    String anchor = a0.toWTFString(exec);
-    anchor.replaceWithLiteral('"', "&quot;");
-
-    return JSValue::encode(jsMakeNontrivialString(exec, "<a name=\"", anchor, "\">", s, "</a>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncLink(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    String s = thisValue.toString(exec)->value(exec);
-    JSValue a0 = exec->argument(0);
-    String linkText = a0.toWTFString(exec);
-    linkText.replaceWithLiteral('"', "&quot;");
-
-    unsigned linkTextSize = linkText.length();
-    unsigned stringSize = s.length();
-    unsigned bufferSize = 15 + linkTextSize + stringSize;
-    // FIXME: Should we have an 8-bit version of this code path too? Or maybe only an 8-bit version?
-    UChar* buffer;
-    PassRefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(bufferSize, buffer);
-    if (!impl)
-        return JSValue::encode(jsUndefined());
-    buffer[0] = '<';
-    buffer[1] = 'a';
-    buffer[2] = ' ';
-    buffer[3] = 'h';
-    buffer[4] = 'r';
-    buffer[5] = 'e';
-    buffer[6] = 'f';
-    buffer[7] = '=';
-    buffer[8] = '"';
-    StringView(linkText).getCharactersWithUpconvert(&buffer[9]);
-    buffer[9 + linkTextSize] = '"';
-    buffer[10 + linkTextSize] = '>';
-    StringView(s).getCharactersWithUpconvert(&buffer[11 + linkTextSize]);
-    buffer[11 + linkTextSize + stringSize] = '<';
-    buffer[12 + linkTextSize + stringSize] = '/';
-    buffer[13 + linkTextSize + stringSize] = 'a';
-    buffer[14 + linkTextSize + stringSize] = '>';
-    return JSValue::encode(jsNontrivialString(exec, impl));
-}
-
 enum {
     TrimLeft = 1,
     TrimRight = 2
@@ -1747,9 +1640,14 @@ enum {
 
 static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKind)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     if (!checkObjectCoercible(thisValue))
-        return throwTypeError(exec);
-    String str = thisValue.toString(exec)->value(exec);
+        return throwTypeError(exec, scope);
+    String str = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, { });
+
     unsigned left = 0;
     if (trimKind & TrimLeft) {
         while (left < str.length() && isStrWhiteSpace(str[left]))
@@ -1765,6 +1663,7 @@ static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKin
     if (left == 0 && right == str.length() && thisValue.isString())
         return thisValue;
 
+    scope.release();
     return jsString(exec, str.substringSharingImpl(left, right - left));
 }
 
@@ -1797,21 +1696,24 @@ static inline unsigned clampAndTruncateToUnsigned(double value, unsigned min, un
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
-    String stringToSearchIn = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String stringToSearchIn = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->argument(0);
-    if (jsDynamicCast<RegExpObject*>(a0))
-        return throwVMTypeError(exec);
+    bool isRegularExpression = isRegExp(vm, exec, a0);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    if (isRegularExpression)
+        return throwVMTypeError(exec, scope, "Argument to String.prototype.startsWith cannot be a RegExp");
 
-    String searchString = a0.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String searchString = a0.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue positionArg = exec->argument(1);
     unsigned start = 0;
@@ -1820,8 +1722,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
     else {
         unsigned length = stringToSearchIn.length();
         start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
 
     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixStartingAt(searchString, start)));
@@ -1829,21 +1730,24 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
-    String stringToSearchIn = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String stringToSearchIn = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->argument(0);
-    if (jsDynamicCast<RegExpObject*>(a0))
-        return throwVMTypeError(exec);
+    bool isRegularExpression = isRegExp(vm, exec, a0);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    if (isRegularExpression)
+        return throwVMTypeError(exec, scope, "Argument to String.prototype.endsWith cannot be a RegExp");
 
-    String searchString = a0.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String searchString = a0.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     unsigned length = stringToSearchIn.length();
 
@@ -1853,107 +1757,175 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
         end = std::max(0, endPositionArg.asInt32());
     else if (!endPositionArg.isUndefined()) {
         end = clampAndTruncateToUnsigned(endPositionArg.toInteger(exec), 0, length);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
 
     return JSValue::encode(jsBoolean(stringToSearchIn.hasInfixEndingAt(searchString, std::min(end, length))));
 }
 
+static EncodedJSValue JSC_HOST_CALL stringIncludesImpl(VM& vm, ExecState* exec, String stringToSearchIn, String searchString, JSValue positionArg)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    unsigned start = 0;
+    if (positionArg.isInt32())
+        start = std::max(0, positionArg.asInt32());
+    else {
+        unsigned length = stringToSearchIn.length();
+        start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    }
+
+    return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
+}
+
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
 
-    String stringToSearchIn = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String stringToSearchIn = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->argument(0);
-    if (jsDynamicCast<RegExpObject*>(a0))
-        return throwVMTypeError(exec);
+    bool isRegularExpression = isRegExp(vm, exec, a0);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    if (isRegularExpression)
+        return throwVMTypeError(exec, scope, "Argument to String.prototype.includes cannot be a RegExp");
 
-    String searchString = a0.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String searchString = a0.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue positionArg = exec->argument(1);
-    unsigned start = 0;
-    if (positionArg.isInt32())
-        start = std::max(0, positionArg.asInt32());
-    else {
-        unsigned length = stringToSearchIn.length();
-        start = clampAndTruncateToUnsigned(positionArg.toInteger(exec), 0, length);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
-    }
 
-    return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
+    scope.release();
+    return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
+}
+
+EncodedJSValue JSC_HOST_CALL builtinStringIncludesInternal(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue thisValue = exec->thisValue();
+    ASSERT(checkObjectCoercible(thisValue));
+
+    String stringToSearchIn = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    JSValue a0 = exec->uncheckedArgument(0);
+    String searchString = a0.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+    JSValue positionArg = exec->argument(1);
+
+    scope.release();
+    return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
+        return throwVMTypeError(exec, scope);
     JSString* string = thisValue.toString(exec);
-    return JSValue::encode(JSStringIterator::create(exec, exec->callee()->globalObject()->stringIteratorStructure(), string));
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    return JSValue::encode(JSStringIterator::create(exec, exec->jsCallee()->globalObject()->stringIteratorStructure(), string));
 }
 
-static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, UNormalizationMode form)
+enum class NormalizationForm {
+    CanonicalComposition,
+    CanonicalDecomposition,
+    CompatibilityComposition,
+    CompatibilityDecomposition
+};
+
+static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLength, NormalizationForm form)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     UErrorCode status = U_ZERO_ERROR;
-    int32_t normalizedStringLength = unorm_normalize(source, sourceLength, form, 0, nullptr, 0, &status);
+    // unorm2_get*Instance() documentation says: "Returns an unmodifiable singleton instance. Do not delete it."
+    const UNormalizer2* normalizer = nullptr;
+    switch (form) {
+    case NormalizationForm::CanonicalComposition:
+        normalizer = unorm2_getNFCInstance(&status);
+        break;
+    case NormalizationForm::CanonicalDecomposition:
+        normalizer = unorm2_getNFDInstance(&status);
+        break;
+    case NormalizationForm::CompatibilityComposition:
+        normalizer = unorm2_getNFKCInstance(&status);
+        break;
+    case NormalizationForm::CompatibilityDecomposition:
+        normalizer = unorm2_getNFKDInstance(&status);
+        break;
+    }
+
+    if (!normalizer || U_FAILURE(status))
+        return throwTypeError(exec, scope);
+
+    int32_t normalizedStringLength = unorm2_normalize(normalizer, source, sourceLength, nullptr, 0, &status);
 
     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
         // The behavior is not specified when normalize fails.
         // Now we throw a type erorr since it seems that the contents of the string are invalid.
-        return throwTypeError(exec);
+        return throwTypeError(exec, scope);
     }
 
     UChar* buffer = nullptr;
-    RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
+    auto impl = StringImpl::tryCreateUninitialized(normalizedStringLength, buffer);
     if (!impl)
-        return throwOutOfMemoryError(exec);
+        return throwOutOfMemoryError(exec, scope);
 
     status = U_ZERO_ERROR;
-    unorm_normalize(source, sourceLength, form, 0, buffer, normalizedStringLength, &status);
+    unorm2_normalize(normalizer, source, sourceLength, buffer, normalizedStringLength, &status);
     if (U_FAILURE(status))
-        return throwTypeError(exec);
+        return throwTypeError(exec, scope);
 
-    return jsString(exec, impl.get());
+    scope.release();
+    return jsString(exec, WTFMove(impl));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec);
-    StringView source = thisValue.toString(exec)->view(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+        return throwVMTypeError(exec, scope);
+    auto viewWithString = thisValue.toString(exec)->viewWithUnderlyingString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    StringView view = viewWithString.view;
 
-    UNormalizationMode form = UNORM_NFC;
+    NormalizationForm form = NormalizationForm::CanonicalComposition;
     // Verify that the argument is provided and is not undefined.
     if (!exec->argument(0).isUndefined()) {
-        String formString = exec->uncheckedArgument(0).toString(exec)->value(exec);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        String formString = exec->uncheckedArgument(0).toWTFString(exec);
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
         if (formString == "NFC")
-            form = UNORM_NFC;
+            form = NormalizationForm::CanonicalComposition;
         else if (formString == "NFD")
-            form = UNORM_NFD;
+            form = NormalizationForm::CanonicalDecomposition;
         else if (formString == "NFKC")
-            form = UNORM_NFKC;
+            form = NormalizationForm::CompatibilityComposition;
         else if (formString == "NFKD")
-            form = UNORM_NFKD;
+            form = NormalizationForm::CompatibilityDecomposition;
         else
-            return throwVMError(exec, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
+            return throwVMError(exec, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
     }
 
-    return JSValue::encode(normalize(exec, source.upconvertedCharacters(), source.length(), form));
+    scope.release();
+    return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));
 }
 
 } // namespace JSC