String.prototype.replace() puts extra '<' in result when a named capture reference...
[WebKit-https.git] / Source / JavaScriptCore / runtime / StringPrototype.cpp
index d8c4ded..97f79fa 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
- *  Copyright (C) 2004-2008, 2013, 2016 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 "BuiltinNames.h"
 #include "ButterflyInlines.h"
 #include "CachedCall.h"
-#include "CopiedSpaceInlines.h"
 #include "Error.h"
-#include "Executable.h"
+#include "FrameTracers.h"
 #include "IntlObject.h"
+#include "JSArray.h"
 #include "JSCBuiltins.h"
 #include "JSCInlines.h"
-#include "JSGlobalObjectFunctions.h"
-#include "JSArray.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>
@@ -51,6 +50,7 @@
 #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>
 
@@ -64,7 +64,6 @@ 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 stringProtoFuncReplaceUsingRegExp(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*);
@@ -105,10 +91,11 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState*);
 
 namespace JSC {
 
-const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &stringPrototypeTable, CREATE_METHOD_TABLE(StringPrototype) };
+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
@@ -116,6 +103,19 @@ const ClassInfo StringPrototype::s_info = { "String", &StringObject::s_info, &st
     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
 */
 
@@ -128,14 +128,13 @@ StringPrototype::StringPrototype(VM& vm, Structure* structure)
 void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSString* nameAndMessage)
 {
     Base::finishCreation(vm, nameAndMessage);
-    ASSERT(inherits(info()));
+    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("concat", stringProtoFuncConcat, 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);
@@ -143,7 +142,7 @@ void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSStr
     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_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("toLowerCase", stringProtoFuncToLowerCase, DontEnum, 0, StringPrototypeToLowerCaseIntrinsic);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
 #if ENABLE(INTL)
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("localeCompare", stringPrototypeLocaleCompareCodeGenerator, DontEnum);
@@ -154,19 +153,6 @@ void StringPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, JSStr
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleLowerCase", stringProtoFuncToLowerCase, DontEnum, 0);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("toLocaleUpperCase", stringProtoFuncToUpperCase, DontEnum, 0);
 #endif
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("big", stringProtoFuncBig, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("small", stringProtoFuncSmall, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("blink", stringProtoFuncBlink, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("bold", stringProtoFuncBold, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fixed", stringProtoFuncFixed, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("italics", stringProtoFuncItalics, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("strike", stringProtoFuncStrike, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sub", stringProtoFuncSub, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("sup", stringProtoFuncSup, DontEnum, 0);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontcolor", stringProtoFuncFontcolor, DontEnum, 1);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fontsize", stringProtoFuncFontsize, DontEnum, 1);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("anchor", stringProtoFuncAnchor, DontEnum, 1);
-    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("link", stringProtoFuncLink, DontEnum, 1);
     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);
@@ -196,6 +182,7 @@ StringPrototype* StringPrototype::create(VM& vm, JSGlobalObject* globalObject, S
 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())
@@ -222,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
@@ -299,6 +320,7 @@ 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.
+        scope.release();
         return jsString(exec, StringImpl::createSubstringSharingImpl(*source.impl(), std::max(0, position), std::min(sourceSize, length)));
     }
 
@@ -324,6 +346,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourc
             }
         }
 
+        scope.release();
         return jsString(exec, WTFMove(impl));
     }
 
@@ -342,6 +365,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourc
         }
     }
 
+    scope.release();
     return jsString(exec, WTFMove(impl));
 }
 
@@ -357,6 +381,7 @@ 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.
+        scope.release();
         return jsString(exec, StringImpl::createSubstringSharingImpl(*source.impl(), std::max(0, position), std::min(sourceSize, length)));
     }
 
@@ -400,6 +425,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
             }
         }        
 
+        scope.release();
         return jsString(exec, WTFMove(impl));
     }
 
@@ -431,11 +457,19 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
         }
     }
 
+    scope.release();
     return jsString(exec, WTFMove(impl));
 }
 
+#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;
@@ -450,9 +484,10 @@ static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* e
         if (!result)
             break;
 
-        if (lastIndex < result.start)
-            sourceRanges.constructAndAppend(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;
 
@@ -467,9 +502,11 @@ static ALWAYS_INLINE EncodedJSValue removeUsingRegExpSearch(VM& vm, ExecState* e
     if (!lastIndex)
         return JSValue::encode(string);
 
-    if (static_cast<unsigned>(lastIndex) < sourceLen)
-        sourceRanges.constructAndAppend(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()));
 }
 
@@ -477,22 +514,25 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
     VM& vm, ExecState* exec, JSString* string, JSValue searchValue, CallData& callData,
     CallType callType, String& replacementString, JSValue replaceValue)
 {
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     const String& source = string->value(exec);
     unsigned sourceLen = source.length();
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    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(jsUndefined());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-        if (callType == CallType::None && !replacementString.length())
+        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.
@@ -511,8 +551,7 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
         int argCount = regExp->numSubpatterns() + 1 + 2;
         JSFunction* func = jsCast<JSFunction*>(replaceValue);
         CachedCall cachedCall(exec, func, argCount);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
         if (source.is8Bit()) {
             while (true) {
                 int* ovector;
@@ -520,27 +559,49 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
                 if (!result)
                     break;
 
-                sourceRanges.constructAndAppend(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())
-                    return JSValue::encode(jsUndefined());
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                replacements.append(jsResult.toWTFString(exec));
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
                 lastIndex = result.end;
                 startPosition = lastIndex;
@@ -559,27 +620,49 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
                 if (!result)
                     break;
 
-                sourceRanges.constructAndAppend(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())
-                    return JSValue::encode(jsUndefined());
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                replacements.append(jsResult.toWTFString(exec));
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
                 lastIndex = result.end;
                 startPosition = lastIndex;
@@ -600,30 +683,54 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
                 break;
 
             if (callType != CallType::None) {
-                sourceRanges.constructAndAppend(lastIndex, result.start - lastIndex);
+                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())
-                    return JSValue::encode(jsUndefined());
+                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.constructAndAppend(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));
@@ -647,9 +754,11 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingRegExpSearch(
     if (!lastIndex && replacements.isEmpty())
         return JSValue::encode(string);
 
-    if (static_cast<unsigned>(lastIndex) < sourceLen)
-        sourceRanges.constructAndAppend(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()));
 }
 
@@ -658,18 +767,20 @@ EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpEmptyStr(
 {
     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);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        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());
 }
@@ -688,25 +799,29 @@ EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceRegExpString(
 
 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.toString(exec)->value(exec);
-        if (exec->hadException())
-            return JSValue::encode(jsUndefined());
+        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&, ExecState* exec, JSString* jsString, JSValue searchValue, JSValue 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);
 
@@ -721,23 +836,22 @@ static ALWAYS_INLINE EncodedJSValue replaceUsingStringSearch(VM&, ExecState* exe
         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));
 
     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));
+    scope.release();
     return JSValue::encode(JSC::jsString(exec, leftPart, middlePart, rightPart));
 }
 
@@ -749,7 +863,7 @@ 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;
@@ -770,26 +884,39 @@ static inline JSString* repeatCharacter(ExecState& exec, CharacterType character
 
     std::fill_n(buffer, repeatCount, character);
 
+    scope.release();
     return jsString(&exec, WTFMove(impl));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     // 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 = jsCast<JSString*>(exec->uncheckedArgument(0));
+    JSString* string = asString(exec->uncheckedArgument(0));
     ASSERT(string->length() == 1);
 
-    if (!exec->uncheckedArgument(1).isInt32())
-        return JSValue::encode(jsNull());
-
-    int32_t repeatCount = exec->uncheckedArgument(1).asInt32();
+    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);
-
-    UChar character = string->view(exec)[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));
@@ -798,7 +925,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncRepeatCharacter(ExecState* exec)
 ALWAYS_INLINE EncodedJSValue replace(
     VM& vm, ExecState* exec, JSString* string, JSValue searchValue, JSValue replaceValue)
 {
-    if (searchValue.inherits(RegExpObject::info()))
+    if (searchValue.inherits(vm, RegExpObject::info()))
         return replaceUsingRegExpSearch(vm, exec, string, searchValue, replaceValue);
     return replaceUsingStringSearch(vm, exec, string, searchValue, replaceValue);
 }
@@ -811,31 +938,37 @@ ALWAYS_INLINE EncodedJSValue replace(
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
     JSString* string = thisValue.toString(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    scope.release();
     return replace(vm, exec, string, searchValue, replaceValue);
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSString* string = exec->thisValue().toString(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue searchValue = exec->argument(0);
-    if (!searchValue.inherits(RegExpObject::info()))
+    if (!searchValue.inherits(vm, RegExpObject::info()))
         return JSValue::encode(jsUndefined());
 
-    return replaceUsingRegExpSearch(exec->vm(), exec, string, searchValue, exec->argument(1));
+    scope.release();
+    return replaceUsingRegExpSearch(vm, exec, string, searchValue, exec->argument(1));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSString* string = exec->thisValue().toString(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-    return replaceUsingStringSearch(exec->vm(), exec, string, exec->argument(0), exec->argument(1));
+    scope.release();
+    return replaceUsingStringSearch(vm, exec, string, exec->argument(0), exec->argument(1));
 }
 
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
@@ -861,7 +994,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec)
     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, scope);
@@ -875,17 +1008,20 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec)
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
-    JSString::SafeView string = thisValue.toString(exec)->view(exec);
+    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));
 }
 
@@ -897,17 +1033,19 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncCharCodeAt(ExecState* exec)
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
-    JSString::SafeView string = thisValue.toString(exec)->view(exec);
+    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());
 }
 
@@ -931,6 +1069,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncCodePointAt(ExecState* exec)
         return throwVMTypeError(exec, scope);
 
     String string = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     unsigned length = string.length();
 
     JSValue argument0 = exec->argument(0);
@@ -941,29 +1080,15 @@ 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)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    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, scope);
-    return JSValue::encode(jsStringFromArguments(exec, thisValue));
-}
-
 EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -977,7 +1102,9 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIndexOf(ExecState* exec)
     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()) {
@@ -998,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).get(), 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));
@@ -1017,8 +1148,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec)
     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;
@@ -1052,9 +1185,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String s = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     int len = s.length();
     RELEASE_ASSERT(len >= 0);
@@ -1082,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>();
@@ -1095,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)
@@ -1111,21 +1247,20 @@ static ALWAYS_INLINE bool splitStringByOneCharacterImpl(ExecState* exec, JSArray
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
 {
     VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue thisValue = exec->thisValue();
     ASSERT(checkObjectCoercible(thisValue));
 
     // 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.toString(exec)->value(exec);
-    if (UNLIKELY(vm.exception()))
-        return JSValue::encode(jsUndefined());
+    String input = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     ASSERT(!input.isNull());
 
     // 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);
-    if (UNLIKELY(vm.exception()))
-        return JSValue::encode(jsUndefined());
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     // 5. Let lengthA be 0.
     unsigned resultLength = 0;
@@ -1140,9 +1275,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
     // 9. If separator is a RegExp object (its [[Class]] is "RegExp"), let R = separator;
     //    otherwise let R = ToString(separator).
     JSValue separatorValue = exec->uncheckedArgument(0);
-    String separator = separatorValue.toString(exec)->value(exec);
-    if (UNLIKELY(vm.exception()))
-        return JSValue::encode(jsUndefined());
+    String separator = separatorValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     // 10. If lim == 0, return A.
     if (!limit)
@@ -1151,6 +1285,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
     // 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);
@@ -1162,8 +1297,10 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
         // b. If z is not false, return A.
         // c. Call CreateDataProperty(A, "0", S).
         // d. Return A.
-        if (!separator.isEmpty())
+        if (!separator.isEmpty()) {
+            scope.release();
             result->putDirectIndex(exec, 0, jsStringWithReuse(exec, thisValue, input));
+        }
         return JSValue::encode(result);
     }
 
@@ -1175,6 +1312,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
 
         do {
             result->putDirectIndex(exec, position, jsSingleCharacterString(exec, input[position]));
+            RETURN_IF_EXCEPTION(scope, encodedJSValue());
         } while (++position < limit);
 
         return JSValue::encode(result);
@@ -1196,12 +1334,17 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
             separatorCharacter = separatorImpl->characters16()[0];
 
         if (stringImpl->is8Bit()) {
-            if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
+            if (splitStringByOneCharacterImpl<LChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit)) {
+                scope.release();
                 return JSValue::encode(result);
+            }
         } else {
-            if (splitStringByOneCharacterImpl<UChar>(exec, result, thisValue, input, stringImpl, separatorCharacter, position, resultLength, limit))
+            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;
@@ -1214,6 +1357,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
             //    through q (exclusive).
             // 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)
@@ -1228,6 +1372,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSplitFast(ExecState* exec)
     // 15. Let T be a String value equal to the substring of S consisting of the characters at positions p (inclusive)
     //     through s (exclusive).
     // 16. Call CreateDataProperty(A, ToString(lengthA), T).
+    scope.release();
     result->putDirectIndex(exec, resultLength++, jsSubstring(exec, thisValue, input, position, input.length() - position));
 
     // 17. Return A.
@@ -1246,12 +1391,11 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec)
     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();
     }
 
@@ -1286,10 +1430,7 @@ EncodedJSValue JSC_HOST_CALL builtinStringSubstrInternal(ExecState* exec)
     // 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.
-#if !ASSERT_DISABLED
-    JSValue thisValue = exec->thisValue();
-    ASSERT(thisValue.isString());
-#endif
+    ASSERT(exec->thisValue().isString());
     return stringProtoFuncSubstr(exec);
 }
 
@@ -1303,8 +1444,7 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* 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);
@@ -1312,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;
@@ -1321,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)
@@ -1345,10 +1487,12 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec)
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
     JSString* sVal = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     const String& s = sVal->value(exec);
     String lowercasedString = s.convertToLowercaseWithoutLocale();
     if (lowercasedString.impl() == s.impl())
         return JSValue::encode(sVal);
+    scope.release();
     return JSValue::encode(jsString(exec, lowercasedString));
 }
 
@@ -1361,10 +1505,12 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec)
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
     JSString* sVal = thisValue.toString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     const String& s = sVal->value(exec);
     String uppercasedString = s.convertToUppercaseWithoutLocale();
     if (uppercasedString.impl() == s.impl())
         return JSValue::encode(sVal);
+    scope.release();
     return JSValue::encode(jsString(exec, uppercasedString));
 }
 
@@ -1376,14 +1522,12 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncLocaleCompare(ExecState* exec)
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String s = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->argument(0);
-    String str = a0.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String str = a0.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     return JSValue::encode(jsNumber(Collator().collate(s, str)));
 }
 
@@ -1400,11 +1544,11 @@ static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UCha
 
     // 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())
@@ -1414,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();
@@ -1470,6 +1613,7 @@ static EncodedJSValue toLocaleCase(ExecState* state, int32_t (*convertCase)(UCha
         return throwVMTypeError(state, scope, u_errorName(error));
 
     // 18. Return L.
+    scope.release();
     return JSValue::encode(jsString(state, lower));
 }
 
@@ -1489,269 +1633,6 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncToLocaleUpperCase(ExecState* state)
 }
 #endif // ENABLE(INTL)
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBig(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<big>", s, "</big>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSmall(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<small>", s, "</small>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBlink(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<blink>", s, "</blink>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncBold(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<b>", s, "</b>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFixed(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<tt>", s, "</tt>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncItalics(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<i>", s, "</i>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncStrike(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<strike>", s, "</strike>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSub(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<sub>", s, "</sub>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncSup(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-    return JSValue::encode(jsMakeNontrivialString(exec, "<sup>", s, "</sup>"));
-}
-
-EncodedJSValue JSC_HOST_CALL stringProtoFuncFontcolor(ExecState* exec)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    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)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    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;
-        auto 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, WTFMove(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)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    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)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue thisValue = exec->thisValue();
-    if (!checkObjectCoercible(thisValue))
-        return throwVMTypeError(exec, scope);
-    String s = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    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;
-    auto 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, WTFMove(impl)));
-}
-
 enum {
     TrimLeft = 1,
     TrimRight = 2
@@ -1764,9 +1645,8 @@ static inline JSValue trimString(ExecState* exec, JSValue thisValue, int trimKin
 
     if (!checkObjectCoercible(thisValue))
         return throwTypeError(exec, scope);
-    String str = thisValue.toString(exec)->value(exec);
-    if (exec->hadException())
-        return jsUndefined();
+    String str = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, { });
 
     unsigned left = 0;
     if (trimKind & TrimLeft) {
@@ -1783,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));
 }
 
@@ -1822,20 +1703,17 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncStartsWith(ExecState* exec)
     if (!checkObjectCoercible(thisValue))
         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);
     bool isRegularExpression = isRegExp(vm, exec, a0);
-    if (vm.exception())
-        return JSValue::encode(JSValue());
+    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;
@@ -1844,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)));
@@ -1860,20 +1737,17 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
     if (!checkObjectCoercible(thisValue))
         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);
     bool isRegularExpression = isRegExp(vm, exec, a0);
-    if (vm.exception())
-        return JSValue::encode(JSValue());
+    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();
 
@@ -1883,8 +1757,7 @@ 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))));
@@ -1892,14 +1765,14 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncEndsWith(ExecState* exec)
 
 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);
-        if (vm.exception())
-            return JSValue::encode(jsUndefined());
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     }
 
     return JSValue::encode(jsBoolean(stringToSearchIn.contains(searchString, true, start)));
@@ -1914,43 +1787,42 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIncludes(ExecState* exec)
     if (!checkObjectCoercible(thisValue))
         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);
     bool isRegularExpression = isRegExp(vm, exec, a0);
-    if (vm.exception())
-        return JSValue::encode(JSValue());
+    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);
 
+    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.toString(exec)->value(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    String stringToSearchIn = thisValue.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
     JSValue a0 = exec->uncheckedArgument(0);
-    VM& vm = exec->vm();
-    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);
 
+    scope.release();
     return stringIncludesImpl(vm, exec, stringToSearchIn, searchString, positionArg);
 }
 
@@ -1963,16 +1835,44 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncIterator(ExecState* exec)
     if (!checkObjectCoercible(thisValue))
         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.
@@ -1986,10 +1886,11 @@ static JSValue normalize(ExecState* exec, const UChar* source, size_t sourceLeng
         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, scope);
 
+    scope.release();
     return jsString(exec, WTFMove(impl));
 }
 
@@ -2001,30 +1902,30 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncNormalize(ExecState* exec)
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec, scope);
-    JSString::SafeView source = thisValue.toString(exec)->view(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
+    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, scope, createRangeError(exec, ASCIILiteral("argument does not match any normalization form")));
     }
 
-    return JSValue::encode(normalize(exec, source.get().upconvertedCharacters(), source.length(), form));
+    scope.release();
+    return JSValue::encode(normalize(exec, view.upconvertedCharacters(), view.length(), form));
 }
 
 } // namespace JSC