Bug 1265307 - Backout selfhosting regexp and ES6 regexp conformity, r=efaust r=till, a=lizzard
authorHannes Verschore <hv1989>
Wed, 04 May 2016 07:21:00 +0200
changeset 332744 b61ad3a6049cbcfe4945897e94b8d7ce5beb5c5f
parent 332743 b14d8e23323e95c5db3bc4cca69738edeee44302
child 332745 b3e8b9ecb15741f046df04d395987084ba87cb13
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust, till, lizzard
bugs1265307
milestone48.0a2
Bug 1265307 - Backout selfhosting regexp and ES6 regexp conformity, r=efaust r=till, a=lizzard
js/src/builtin/Intl.js
js/src/builtin/RegExp.cpp
js/src/builtin/RegExp.h
js/src/builtin/RegExp.js
js/src/builtin/SelfHostingDefines.h
js/src/builtin/String.js
js/src/devtools/automation/cgc-jittest-timeouts.txt
js/src/jit-test/tests/basic/bug754150.js
js/src/jit-test/tests/ion/bug977966.js
js/src/jit-test/tests/ion/dce-with-rinstructions.js
js/src/jit-test/tests/ion/testObjectHasPrototype.js
js/src/jit-test/tests/ion/testStringMatch.js
js/src/jit-test/tests/self-hosting/GetStringDataProperty.js
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineInspector.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/InlinableNatives.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.h
js/src/jit/JitCompartment.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/Recover.cpp
js/src/jit/Recover.h
js/src/jit/SharedIC.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm64/Assembler-arm64.h
js/src/jit/mips-shared/Assembler-mips-shared.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/js.msg
js/src/jsapi.h
js/src/jscompartment.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/tests/ecma_3/String/15.5.4.11.js
js/src/tests/ecma_5/String/split-01.js
js/src/tests/ecma_6/RegExp/RegExpExec-exec.js
js/src/tests/ecma_6/RegExp/RegExpExec-return.js
js/src/tests/ecma_6/RegExp/constructor-constructor.js
js/src/tests/ecma_6/RegExp/match-this.js
js/src/tests/ecma_6/RegExp/match-trace.js
js/src/tests/ecma_6/RegExp/match.js
js/src/tests/ecma_6/RegExp/replace-sticky-lastIndex.js
js/src/tests/ecma_6/RegExp/replace-sticky.js
js/src/tests/ecma_6/RegExp/replace-this.js
js/src/tests/ecma_6/RegExp/replace-trace.js
js/src/tests/ecma_6/RegExp/replace.js
js/src/tests/ecma_6/RegExp/search-this.js
js/src/tests/ecma_6/RegExp/search-trace.js
js/src/tests/ecma_6/RegExp/search.js
js/src/tests/ecma_6/RegExp/split-invalid-lastIndex.js
js/src/tests/ecma_6/RegExp/split-obj.js
js/src/tests/ecma_6/RegExp/split-this.js
js/src/tests/ecma_6/RegExp/split-trace.js
js/src/tests/ecma_6/RegExp/split.js
js/src/tests/ecma_6/String/match.js
js/src/tests/ecma_6/String/replace.js
js/src/tests/ecma_6/String/search.js
js/src/tests/ecma_6/String/split.js
js/src/tests/ecma_6/Symbol/species.js
js/src/tests/ecma_6/Symbol/well-known.js
js/src/tests/ecma_6/extensions/RegExp-replace-lastParen.js
js/src/vm/CommonPropertyNames.h
js/src/vm/GlobalObject.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
js/src/vm/SelfHosting.cpp
js/src/vm/SelfHosting.h
js/xpconnect/tests/chrome/test_xrayToJS.xul
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -101,17 +101,17 @@ function removeUnicodeExtensions(locale)
         pos = locale.length;
 
     var left = callFunction(String_substring, locale, 0, pos);
     var right = callFunction(String_substring, locale, pos);
 
     var extensions;
     var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE();
     while ((extensions = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, left)) !== null) {
-        left = callFunction(String_replace, left, extensions[0], "");
+        left = callFunction(std_String_replace, left, extensions[0], "");
         unicodeLocaleExtensionSequenceRE.lastIndex = 0;
     }
 
     var combined = left + right;
     assert(IsStructurallyValidLanguageTag(combined), "recombination produced an invalid language tag");
     assert(function() {
         var uindex = callFunction(std_String_indexOf, combined, "-u-");
         if (uindex < 0)
@@ -373,17 +373,17 @@ function CanonicalizeLanguageTag(locale)
     // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
     // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
     locale = callFunction(std_String_toLowerCase, locale);
 
     // Handle mappings for complete tags.
     if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
         return langTagMappings[locale];
 
-    var subtags = StringSplitString(ToString(locale), "-");
+    var subtags = callFunction(std_String_split, locale, "-");
     var i = 0;
 
     // Handle the standard part: All subtags before the first singleton or "x".
     // "zh-nan-hans-bu-variant2-variant1"
     while (i < subtags.length) {
         var subtag = subtags[i];
 
         // If we reach the start of an extension sequence or private use part,
@@ -832,17 +832,17 @@ function ResolveLocale(availableLocales,
     var extensionIndex, extensionSubtags, extensionSubtagsLength;
 
     // Step 5.
     if (extension !== undefined) {
         // Step 5.b.
         extensionIndex = r.extensionIndex;
 
         // Steps 5.d-e.
-        extensionSubtags = StringSplitString(ToString(extension), "-");
+        extensionSubtags = callFunction(std_String_split, extension, "-");
         extensionSubtagsLength = extensionSubtags.length;
     }
 
     // Steps 6-7.
     var result = new Record();
     result.dataLocale = foundLocale;
 
     // Step 8.
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/RegExp.h"
 
-#include "mozilla/CheckedInt.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jscntxt.h"
 
 #include "irregexp/RegExpParser.h"
 #include "jit/InlinableNatives.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
@@ -19,24 +18,20 @@
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::unicode;
 
-using mozilla::CheckedInt;
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 16-25.
- */
+/* ES6 21.2.5.2.2 steps 19-29. */
 bool
 js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                             MutableHandleValue rval)
 {
     MOZ_ASSERT(input);
 
     /*
      * Create the (slow) result array for a match.
@@ -51,22 +46,22 @@ js::CreateRegExpMatchResult(JSContext* c
     /* Get the templateObject that defines the shape and type of the output object */
     JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return false;
 
     size_t numPairs = matches.length();
     MOZ_ASSERT(numPairs > 0);
 
-    /* Step 17. */
+    /* Step 19. */
     RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
     if (!arr)
         return false;
 
-    /* Steps 22-24.
+    /* Steps 27-28.
      * Store a Value for each pair. */
     for (size_t i = 0; i < numPairs; i++) {
         const MatchPair& pair = matches[i];
 
         if (pair.isUndefined()) {
             MOZ_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, UndefinedValue());
@@ -74,69 +69,55 @@ js::CreateRegExpMatchResult(JSContext* c
             JSLinearString* str = NewDependentString(cx, input, pair.start, pair.length());
             if (!str)
                 return false;
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, StringValue(str));
         }
     }
 
-    /* Step 20 (reordered).
+    /* Step 24 (reordered).
      * Set the |index| property. (TemplateObject positions it in slot 0) */
     arr->setSlot(0, Int32Value(matches[0].start));
 
-    /* Step 21 (reordered).
+    /* Step 25 (reordered).
      * Set the |input| property. (TemplateObject positions it in slot 1) */
     arr->setSlot(1, StringValue(input));
 
 #ifdef DEBUG
     RootedValue test(cx);
     RootedId id(cx, NameToId(cx->names().index));
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
     MOZ_ASSERT(test == arr->getSlot(0));
     id = NameToId(cx->names().input);
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
     MOZ_ASSERT(test == arr->getSlot(1));
 #endif
 
-    /* Step 25. */
+    /* Step 29. */
     rval.setObject(*arr);
     return true;
 }
 
-static int32_t
-CreateRegExpSearchResult(JSContext* cx, const MatchPairs& matches)
-{
-    /* Fit the start and limit of match into a int32_t. */
-    uint32_t position = matches[0].start;
-    uint32_t lastIndex = matches[0].limit;
-    MOZ_ASSERT(position < 0x8000);
-    MOZ_ASSERT(lastIndex < 0x8000);
-    return position | (lastIndex << 15);
-}
-
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-14, except 12.a.i, 12.c.i.1.
- */
+/* ES6 21.2.5.2.2 steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
 static RegExpRunStatus
 ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
-                  size_t searchIndex, MatchPairs* matches, size_t* endIndex)
+                  size_t searchIndex, bool sticky, MatchPairs* matches, size_t* endIndex)
 {
-    RegExpRunStatus status = re.execute(cx, input, searchIndex, matches, endIndex);
+    RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, matches, endIndex);
 
     /* Out of spec: Update RegExpStatics. */
     if (status == RegExpRunStatus_Success && res) {
         if (matches) {
             if (!res->updateFromMatchPairs(cx, input, *matches))
                 return RegExpRunStatus_Error;
         } else {
-            res->updateLazily(cx, input, &re, searchIndex);
+            res->updateLazily(cx, input, &re, searchIndex, sticky);
         }
     }
     return status;
 }
 
 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
 bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj,
@@ -144,17 +125,17 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
                         MutableHandleValue rval)
 {
     RegExpGuard shared(cx);
     if (!reobj.getShared(cx, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex,
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, reobj.sticky(),
                                                &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
         rval.setNull();
         return true;
@@ -166,98 +147,59 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
         /* Forbid an array, as an optimization. */
         rval.setBoolean(true);
         return true;
     }
 
     return CreateRegExpMatchResult(cx, input, matches, rval);
 }
 
-enum RegExpSharedUse {
-    UseRegExpShared,
-    DontUseRegExpShared
-};
-
 /*
- * ES 2016 draft Mar 25, 2016 21.2.3.2.2.
- * Because this function only ever returns |obj| in the spec, provided by the
- * user, we omit it and just return the usual success/failure.
+ * ES6 21.2.3.2.2.  Because this function only ever returns |obj| in the spec,
+ * provided by the user, we omit it and just return the usual success/failure.
  */
 static bool
 RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
-                                  HandleValue patternValue, HandleValue flagsValue,
-                                  RegExpSharedUse sharedUse = DontUseRegExpShared)
+                                  HandleValue patternValue, HandleValue flagsValue)
 {
     RootedAtom pattern(cx);
     if (patternValue.isUndefined()) {
         /* Step 1. */
         pattern = cx->names().empty;
     } else {
-        /* Step 2. */
+        /* Steps 2-3. */
         pattern = ToAtom<CanGC>(cx, patternValue);
         if (!pattern)
             return false;
     }
 
-    /* Step 3. */
+    /* Step 4. */
     RegExpFlag flags = RegExpFlag(0);
     if (!flagsValue.isUndefined()) {
-        /* Step 4. */
+        /* Steps 5-6. */
         RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue));
         if (!flagStr)
             return false;
 
-        /* Step 5. */
+        /* Step 7. */
         if (!ParseRegExpFlags(cx, flagStr, &flags))
             return false;
     }
 
-    if (sharedUse == UseRegExpShared) {
-        /* Steps 7-8. */
-        RegExpGuard re(cx);
-        if (!cx->compartment()->regExps.get(cx, pattern, flags, &re))
-            return false;
-
-        /* Steps 9-12. */
-        obj->initIgnoringLastIndex(pattern, flags);
-
-        obj->setShared(*re);
-    } else {
-        /* Steps 7-8. */
-        CompileOptions options(cx);
-        frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
-        if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
-                                          flags & UnicodeFlag))
-        {
-            return false;
-        }
-
-        /* Steps 9-12. */
-        obj->initIgnoringLastIndex(pattern, flags);
+    /* Steps 8-10. */
+    CompileOptions options(cx);
+    frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
+    if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
+                                      flags & UnicodeFlag))
+    {
+        return false;
     }
 
-    return true;
-}
-
-/* ES 2016 draft Mar 25, 2016 21.2.3.2.3. */
-bool
-js::RegExpCreate(JSContext* cx, HandleValue patternValue, HandleValue flagsValue,
-                 MutableHandleValue rval)
-{
-    /* Step 1. */
-    Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, nullptr));
-    if (!regexp)
-         return false;
-
-    /* Step 2. */
-    if (!RegExpInitializeIgnoringLastIndex(cx, regexp, patternValue, flagsValue, UseRegExpShared))
-        return false;
-    regexp->zeroLastIndex(cx);
-
-    rval.setObject(*regexp);
+    /* Steps 11-13. */
+    obj->initIgnoringLastIndex(pattern, flags);
     return true;
 }
 
 MOZ_ALWAYS_INLINE bool
 IsRegExpObject(HandleValue v)
 {
     return v.isObject() && v.toObject().is<RegExpObject>();
 }
@@ -359,89 +301,87 @@ static bool
 regexp_compile(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Steps 1-2. */
     return CallNonGenericMethod<IsRegExpObject, regexp_compile_impl>(cx, args);
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.3.1.
- */
+/* ES6 21.2.3.1. */
 bool
 js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    // Steps 1.
+    // Steps 1-2.
     bool patternIsRegExp;
     if (!IsRegExp(cx, args.get(0), &patternIsRegExp))
         return false;
 
     // We can delay step 3 and step 4a until later, during
     // GetPrototypeFromCallableConstructor calls. Accessing the new.target
     // and the callee from the stack is unobservable.
     if (!args.isConstructing()) {
-        // Step 3.b.
+        // Step 4b.
         if (patternIsRegExp && !args.hasDefined(1)) {
             RootedObject patternObj(cx, &args[0].toObject());
 
-            // Step 3.b.i.
+            // Step 4b.i-ii.
             RootedValue patternConstructor(cx);
             if (!GetProperty(cx, patternObj, patternObj, cx->names().constructor, &patternConstructor))
                 return false;
 
-            // Step 3.b.ii.
+            // Step 4b.iii.
             if (patternConstructor.isObject() && patternConstructor.toObject() == args.callee()) {
                 args.rval().set(args[0]);
                 return true;
             }
         }
     }
 
     RootedValue patternValue(cx, args.get(0));
 
-    // Step 4.
+    // Step 5.
     ESClassValue cls;
     if (!GetClassOfValue(cx, patternValue, &cls))
         return false;
     if (cls == ESClass_RegExp) {
         // Beware!  |patternObj| might be a proxy into another compartment, so
         // don't assume |patternObj.is<RegExpObject>()|.  For the same reason,
         // don't reuse the RegExpShared below.
         RootedObject patternObj(cx, &patternValue.toObject());
 
         RootedAtom sourceAtom(cx);
         RegExpFlag flags;
         {
-            // Step 4.a.
+            // Step 5.a.
             RegExpGuard g(cx);
             if (!RegExpToShared(cx, patternObj, &g))
                 return false;
             sourceAtom = g->getSource();
 
             if (!args.hasDefined(1)) {
-                // Step 4.b.
+                // Step 5.b.
                 flags = g->getFlags();
             }
         }
 
-        // Step 7.
+        // Step 8-9.
         RootedObject proto(cx);
         if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
             return false;
 
         Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto));
         if (!regexp)
             return false;
 
-        // Step 8.
+        // Step 10.
         if (args.hasDefined(1)) {
-            // Step 4.c / 21.2.3.2.2 RegExpInitialize step 4.
+            // Step 5c / 21.2.3.2.2 RegExpInitialize step 5.
             flags = RegExpFlag(0);
             RootedString flagStr(cx, ToString<CanGC>(cx, args[1]));
             if (!flagStr)
                 return false;
             if (!ParseRegExpFlags(cx, flagStr, &flags))
                 return false;
         }
 
@@ -449,46 +389,46 @@ js::regexp_construct(JSContext* cx, unsi
 
         args.rval().setObject(*regexp);
         return true;
     }
 
     RootedValue P(cx);
     RootedValue F(cx);
 
-    // Step 5.
+    // Step 6.
     if (patternIsRegExp) {
         RootedObject patternObj(cx, &patternValue.toObject());
 
-        // Step 5.a.
+        // Step 6a-b.
         if (!GetProperty(cx, patternObj, patternObj, cx->names().source, &P))
             return false;
 
-        // Step 5.b.
+        // Step 6c-d.
         F = args.get(1);
         if (F.isUndefined()) {
             if (!GetProperty(cx, patternObj, patternObj, cx->names().flags, &F))
                 return false;
         }
     } else {
-        // Steps 6.a-b.
+        // Steps 7a-b.
         P = patternValue;
         F = args.get(1);
     }
 
-    // Step 7.
+    // Step 8-9.
     RootedObject proto(cx);
     if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
         return false;
 
     Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto));
     if (!regexp)
         return false;
 
-    // Step 8.
+    // Step 10.
     if (!RegExpInitializeIgnoringLastIndex(cx, regexp, P, F))
         return false;
     regexp->zeroLastIndex(cx);
 
     args.rval().setObject(*regexp);
     return true;
 }
 
@@ -512,53 +452,16 @@ js::regexp_construct_self_hosting(JSCont
     if (!RegExpInitializeIgnoringLastIndex(cx, regexp, args[0], args.get(1)))
         return false;
     regexp->zeroLastIndex(cx);
 
     args.rval().setObject(*regexp);
     return true;
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.3.1
- * steps 4, 7-8.
- * Ignore sticky flag of flags argument, for optimized path in @@split.
- */
-bool
-js::regexp_construct_no_sticky(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(!args.isConstructing());
-
-    Rooted<RegExpObject*> rx(cx, &args[0].toObject().as<RegExpObject>());
-
-    // Step 4.a.
-    RootedAtom sourceAtom(cx, rx->getSource());
-
-    // Step 4.c.
-    RootedString flagStr(cx, args[1].toString());
-    RegExpFlag flags = RegExpFlag(0);
-    if (!ParseRegExpFlags(cx, flagStr, &flags))
-        return false;
-
-    // Ignore sticky flag.
-    flags = RegExpFlag(flags & ~StickyFlag);
-
-    // Step 7.
-    Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx));
-    if (!regexp)
-        return false;
-
-    // Step 8.
-    regexp->initAndZeroLastIndex(sourceAtom, flags, cx);
-    args.rval().setObject(*regexp);
-    return true;
-}
-
 /* ES6 draft rev32 21.2.5.4. */
 MOZ_ALWAYS_INLINE bool
 regexp_global_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsRegExpObject(args.thisv()));
     Rooted<RegExpObject*> reObj(cx, &args.thisv().toObject().as<RegExpObject>());
 
     /* Steps 4-6. */
@@ -695,20 +598,16 @@ const JSPropertySpec js::regexp_properti
 const JSFunctionSpec js::regexp_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_SELF_HOSTED_FN(js_toSource_str, "RegExpToString", 0, 0),
 #endif
     JS_SELF_HOSTED_FN(js_toString_str, "RegExpToString", 0, 0),
     JS_FN("compile",        regexp_compile,     2,0),
     JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0),
     JS_SELF_HOSTED_FN("test", "RegExpTest" ,    1,0),
-    JS_SELF_HOSTED_SYM_FN(match, "RegExpMatch", 1,0),
-    JS_SELF_HOSTED_SYM_FN(replace, "RegExpReplace", 2,0),
-    JS_SELF_HOSTED_SYM_FN(search, "RegExpSearch", 1,0),
-    JS_SELF_HOSTED_SYM_FN(split, "RegExpSplit", 2,0),
     JS_FS_END
 };
 
 #define STATIC_PAREN_GETTER_CODE(parenNum)                                      \
     if (!res->createParen(cx, parenNum, args.rval()))                           \
         return false;                                                           \
     if (args.rval().isUndefined())                                              \
         args.rval().setString(cx->runtime()->emptyString);                      \
@@ -797,17 +696,16 @@ const JSPropertySpec js::regexp_static_p
     JS_PSG("$7", static_paren7_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     JS_PSG("$8", static_paren8_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     JS_PSG("$9", static_paren9_getter, JSPROP_PERMANENT | JSPROP_ENUMERATE),
     JS_PSGS("$_", static_input_getter, static_input_setter, JSPROP_PERMANENT),
     JS_PSG("$&", static_lastMatch_getter, JSPROP_PERMANENT),
     JS_PSG("$+", static_lastParen_getter, JSPROP_PERMANENT),
     JS_PSG("$`", static_leftContext_getter, JSPROP_PERMANENT),
     JS_PSG("$'", static_rightContext_getter, JSPROP_PERMANENT),
-    JS_SELF_HOSTED_SYM_GET(species, "RegExpSpecies", 0),
     JS_PS_END
 };
 
 JSObject*
 js::CreateRegExpPrototype(JSContext* cx, JSProtoKey key)
 {
     MOZ_ASSERT(key == JSProto_RegExp);
 
@@ -843,23 +741,20 @@ IsTrailSurrogateWithLeadSurrogate(JSCont
     if (index <= 0 || size_t(index) >= input->length())
         return false;
 
     return input->hasLatin1Chars()
            ? IsTrailSurrogateWithLeadSurrogateImpl<Latin1Char>(cx, input, index)
            : IsTrailSurrogateWithLeadSurrogateImpl<char16_t>(cx, input, index);
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-14, except 12.a.i, 12.c.i.1.
- */
+/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
 static RegExpRunStatus
 ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
-              int32_t lastIndex,
+              int32_t lastIndex, bool sticky,
               MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
 {
     /*
      * WARNING: Despite the presence of spec step comment numbers, this
      *          algorithm isn't consistent with any ES6 version, draft or
      *          otherwise.  YOU HAVE BEEN WARNED.
      */
 
@@ -881,23 +776,22 @@ ExecuteRegExp(JSContext* cx, HandleObjec
 
     RootedLinearString input(cx, string->ensureLinear(cx));
     if (!input)
         return RegExpRunStatus_Error;
 
     /* Handled by caller */
     MOZ_ASSERT(lastIndex >= 0 && size_t(lastIndex) <= input->length());
 
-    /* Steps 4-8 performed by the caller. */
+    /* Steps 4-10 performed by the caller. */
 
-    /* Step 10. */
+    /* Step 12-13. */
     if (reobj->unicode()) {
         /*
-         * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad
-         * 21.2.2.2 step 2.
+         * ES6 21.2.2.2 step 2.
          *   Let listIndex be the index into Input of the character that was
          *   obtained from element index of str.
          *
          * In the spec, pattern match is performed with decoded Unicode code
          * points, but our implementation performs it with UTF-16 encoded
          * string.  In step 2, we should decrement lastIndex (index) if it
          * points the trail surrogate that has corresponding lead surrogate.
          *
@@ -911,217 +805,133 @@ ExecuteRegExp(JSContext* cx, HandleObjec
          * different values for `result.index` under certain conditions.
          * However, the spec will change to match our implementation's
          * behavior. See https://github.com/tc39/ecma262/issues/128.
          */
         if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
             lastIndex--;
     }
 
-    /* Steps 3, 11-14, except 12.a.i, 12.c.i.1. */
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, matches, endIndex);
+    /* Steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, sticky, matches, endIndex);
     if (status == RegExpRunStatus_Error)
         return RegExpRunStatus_Error;
 
-    /* Steps 12.a.i, 12.c.i.i, 15 are done by Self-hosted function. */
+    /* Steps 15.a.i-ii, 18 are done by Self-hosted function. */
 
     return status;
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
- */
+/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
 static bool
 RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
-                  int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
+                  int32_t lastIndex, bool sticky,
+                  RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
 {
     /* Execute regular expression and gather matches. */
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
+    /* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, sticky,
                                            &matches, nullptr, staticsUpdate);
     if (status == RegExpRunStatus_Error)
         return false;
 
-    /* Steps 12.a, 12.c. */
+    /* Steps 15.a, 15.c. */
     if (status == RegExpRunStatus_Success_NotFound) {
         rval.setNull();
         return true;
     }
 
-    /* Steps 16-25 */
+    /* Steps 19-29 */
     return CreateRegExpMatchResult(cx, string, matches, rval);
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
- */
+/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
 bool
 js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
+    MOZ_ASSERT(args.length() == 4);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
     MOZ_ASSERT(args[2].isNumber());
+    MOZ_ASSERT(args[3].isBoolean());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
     RootedValue lastIndexVal(cx, args[2]);
+    bool sticky = ToBoolean(args[3]);
 
     int32_t lastIndex = 0;
     if (!ToInt32(cx, lastIndexVal, &lastIndex))
         return false;
 
-    /* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
-    return RegExpMatcherImpl(cx, regexp, string, lastIndex,
+    /* Steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
+    return RegExpMatcherImpl(cx, regexp, string, lastIndex, sticky,
                              UpdateRegExpStatics, args.rval());
 }
 
 /*
  * Separate interface for use by IonMonkey.
  * This code cannot re-enter Ion code.
  */
 bool
 js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                     int32_t lastIndex,
+                     int32_t lastIndex, bool sticky,
                      MatchPairs* maybeMatches, MutableHandleValue output)
 {
     MOZ_ASSERT(lastIndex >= 0);
 
     // The MatchPairs will always be passed in, but RegExp execution was
     // successful only if the pairs have actually been filled in.
     if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
         return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
-    return RegExpMatcherImpl(cx, regexp, input, lastIndex,
+    return RegExpMatcherImpl(cx, regexp, input, lastIndex, sticky,
                              UpdateRegExpStatics, output);
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
- * This code is inlined in CodeGenerator.cpp generateRegExpSearcherStub,
- * changes to this code need to get reflected in there too.
- */
-static bool
-RegExpSearcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
-                   int32_t lastIndex, RegExpStaticsUpdate staticsUpdate, int32_t* result)
-{
-    /* Execute regular expression and gather matches. */
-    ScopedMatchPairs matches(&cx->tempLifoAlloc());
-
-    /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
-                                           &matches, nullptr, staticsUpdate);
-    if (status == RegExpRunStatus_Error)
-        return false;
-
-    /* Steps 12.a, 12.c. */
-    if (status == RegExpRunStatus_Success_NotFound) {
-        *result = -1;
-        return true;
-    }
-
-    /* Steps 16-25 */
-    *result = CreateRegExpSearchResult(cx, matches);
-    return true;
-}
-
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
- */
-bool
-js::RegExpSearcher(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
-    MOZ_ASSERT(IsRegExpObject(args[0]));
-    MOZ_ASSERT(args[1].isString());
-    MOZ_ASSERT(args[2].isNumber());
-
-    RootedObject regexp(cx, &args[0].toObject());
-    RootedString string(cx, args[1].toString());
-    RootedValue lastIndexVal(cx, args[2]);
-
-    int32_t lastIndex = 0;
-    if (!ToInt32(cx, lastIndexVal, &lastIndex))
-        return false;
-
-    /* Steps 3, 9-25, except 12.a.i, 12.c.i.1, 15. */
-    int32_t result = 0;
-    if (!RegExpSearcherImpl(cx, regexp, string, lastIndex, UpdateRegExpStatics, &result))
-        return false;
-
-    args.rval().setInt32(result);
-    return true;
-}
-
-/*
- * Separate interface for use by IonMonkey.
- * This code cannot re-enter Ion code.
- */
-bool
-js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                      int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result)
-{
-    MOZ_ASSERT(lastIndex >= 0);
-
-    // The MatchPairs will always be passed in, but RegExp execution was
-    // successful only if the pairs have actually been filled in.
-    if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0) {
-        *result = CreateRegExpSearchResult(cx, *maybeMatches);
-        return true;
-    }
-    return RegExpSearcherImpl(cx, regexp, input, lastIndex,
-                              UpdateRegExpStatics, result);
-}
-
 bool
 js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
-    return RegExpMatcherImpl(cx, regexp, string, 0,
+    return RegExpMatcherImpl(cx, regexp, string, 0, false,
                              DontUpdateRegExpStatics, args.rval());
 }
 
-/*
- * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
- * steps 3, 9-14, except 12.a.i, 12.c.i.1.
- */
+/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
 bool
 js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
+    MOZ_ASSERT(args.length() == 4);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
     MOZ_ASSERT(args[2].isNumber());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
     RootedValue lastIndexVal(cx, args[2]);
+    bool sticky = ToBoolean(args[3]);
 
     int32_t lastIndex = 0;
     if (!ToInt32(cx, lastIndexVal, &lastIndex))
         return false;
 
-    /* Steps 3, 9-14, except 12.a.i, 12.c.i.1. */
+    /* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
     size_t endIndex = 0;
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex,
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string,
+                                           lastIndex, sticky,
                                            nullptr, &endIndex, UpdateRegExpStatics);
 
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success) {
         MOZ_ASSERT(endIndex <= INT32_MAX);
         args.rval().setInt32(int32_t(endIndex));
@@ -1132,22 +942,22 @@ js::RegExpTester(JSContext* cx, unsigned
 }
 
 /*
  * Separate interface for use by IonMonkey.
  * This code cannot re-enter Ion code.
  */
 bool
 js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                    int32_t lastIndex, int32_t* endIndex)
+                    int32_t lastIndex, bool sticky, int32_t* endIndex)
 {
     MOZ_ASSERT(lastIndex >= 0);
 
     size_t endIndexTmp = 0;
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex,
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex, sticky,
                                            nullptr, &endIndexTmp, UpdateRegExpStatics);
 
     if (status == RegExpRunStatus_Success) {
         MOZ_ASSERT(endIndexTmp <= INT32_MAX);
         *endIndex = int32_t(endIndexTmp);
         return true;
     }
     if (status == RegExpRunStatus_Success_NotFound) {
@@ -1165,521 +975,14 @@ js::regexp_test_no_statics(JSContext* cx
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
     size_t ignored = 0;
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0,
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0, false,
                                            nullptr, &ignored, DontUpdateRegExpStatics);
     args.rval().setBoolean(status == RegExpRunStatus_Success);
     return status != RegExpRunStatus_Error;
 }
 
-static void
-GetParen(JSLinearString* matched, JS::Value capture, JSSubString* out)
-{
-    if (capture.isUndefined()) {
-        out->initEmpty(matched);
-        return;
-    }
-    JSLinearString& captureLinear = capture.toString()->asLinear();
-    out->init(&captureLinear, 0, captureLinear.length());
-}
-
-template <typename CharT>
-static bool
-InterpretDollar(JSLinearString* matched, JSLinearString* string, size_t position, size_t tailPos,
-                AutoValueVector& captures, JSLinearString* replacement,
-                const CharT* replacementBegin, const CharT* currentDollar,
-                const CharT* replacementEnd,
-                JSSubString* out, size_t* skip)
-{
-    MOZ_ASSERT(*currentDollar == '$');
-
-    /* If there is only a dollar, bail now. */
-    if (currentDollar + 1 >= replacementEnd)
-        return false;
-
-    /* ES 2016 draft Mar 25, 2016 Table 46. */
-    char16_t c = currentDollar[1];
-    if (JS7_ISDEC(c)) {
-        /* $n, $nn */
-        unsigned num = JS7_UNDEC(c);
-        if (num > captures.length()) {
-            // The result is implementation-defined, do not substitute.
-            return false;
-        }
-
-        const CharT* currentChar = currentDollar + 2;
-        if (currentChar < replacementEnd && (c = *currentChar, JS7_ISDEC(c))) {
-            unsigned tmpNum = 10 * num + JS7_UNDEC(c);
-            // If num > captures.length(), the result is implementation-defined.
-            // Consume next character only if num <= captures.length().
-            if (tmpNum <= captures.length()) {
-                currentChar++;
-                num = tmpNum;
-            }
-        }
-        if (num == 0) {
-            // The result is implementation-defined.
-            // Do not substitute.
-            return false;
-        }
-
-        *skip = currentChar - currentDollar;
-
-        MOZ_ASSERT(num <= captures.length());
-
-        GetParen(matched, captures[num -1], out);
-        return true;
-    }
-
-    *skip = 2;
-    switch (c) {
-      default:
-        return false;
-      case '$':
-        out->init(replacement, currentDollar - replacementBegin, 1);
-        break;
-      case '&':
-        out->init(matched, 0, matched->length());
-        break;
-      case '+':
-        // SpiderMonkey extension
-        if (captures.length() == 0)
-            out->initEmpty(matched);
-        else
-            GetParen(matched, captures[captures.length() - 1], out);
-        break;
-      case '`':
-        out->init(string, 0, position);
-        break;
-      case '\'':
-        out->init(string, tailPos, string->length() - tailPos);
-        break;
-    }
-    return true;
-}
-
-template <typename CharT>
-static bool
-FindReplaceLengthString(JSContext* cx, HandleLinearString matched, HandleLinearString string,
-                        size_t position, size_t tailPos, AutoValueVector& captures,
-                        HandleLinearString replacement, size_t firstDollarIndex, size_t* sizep)
-{
-    CheckedInt<uint32_t> replen = replacement->length();
-
-    JS::AutoCheckCannotGC nogc;
-    MOZ_ASSERT(firstDollarIndex < replacement->length());
-    const CharT* replacementBegin = replacement->chars<CharT>(nogc);
-    const CharT* currentDollar = replacementBegin + firstDollarIndex;
-    const CharT* replacementEnd = replacementBegin + replacement->length();
-    do {
-        JSSubString sub;
-        size_t skip;
-        if (InterpretDollar(matched, string, position, tailPos, captures, replacement,
-                            replacementBegin, currentDollar, replacementEnd, &sub, &skip))
-        {
-            if (sub.length > skip)
-                replen += sub.length - skip;
-            else
-                replen -= skip - sub.length;
-            currentDollar += skip;
-        } else {
-            currentDollar++;
-        }
-
-        currentDollar = js_strchr_limit(currentDollar, '$', replacementEnd);
-    } while (currentDollar);
-
-    if (!replen.isValid()) {
-        ReportAllocationOverflow(cx);
-        return false;
-    }
-
-    *sizep = replen.value();
-    return true;
-}
-
-static bool
-FindReplaceLength(JSContext* cx, HandleLinearString matched, HandleLinearString string,
-                  size_t position, size_t tailPos, AutoValueVector& captures,
-                  HandleLinearString replacement, size_t firstDollarIndex, size_t* sizep)
-{
-    return replacement->hasLatin1Chars()
-           ? FindReplaceLengthString<Latin1Char>(cx, matched, string, position, tailPos, captures,
-                                                 replacement, firstDollarIndex, sizep)
-           : FindReplaceLengthString<char16_t>(cx, matched, string, position, tailPos, captures,
-                                               replacement, firstDollarIndex, sizep);
-}
-
-/*
- * Precondition: |sb| already has necessary growth space reserved (as
- * derived from FindReplaceLength), and has been inflated to TwoByte if
- * necessary.
- */
-template <typename CharT>
-static void
-DoReplace(HandleLinearString matched, HandleLinearString string,
-          size_t position, size_t tailPos, AutoValueVector& captures,
-          HandleLinearString replacement, size_t firstDollarIndex, StringBuffer &sb)
-{
-    JS::AutoCheckCannotGC nogc;
-    const CharT* replacementBegin = replacement->chars<CharT>(nogc);
-    const CharT* currentChar = replacementBegin;
-
-    MOZ_ASSERT(firstDollarIndex < replacement->length());
-    const CharT* currentDollar = replacementBegin + firstDollarIndex;
-    const CharT* replacementEnd = replacementBegin + replacement->length();
-    do {
-        /* Move one of the constant portions of the replacement value. */
-        size_t len = currentDollar - currentChar;
-        sb.infallibleAppend(currentChar, len);
-        currentChar = currentDollar;
-
-        JSSubString sub;
-        size_t skip;
-        if (InterpretDollar(matched, string, position, tailPos, captures, replacement,
-                            replacementBegin, currentDollar, replacementEnd, &sub, &skip))
-        {
-            sb.infallibleAppendSubstring(sub.base, sub.offset, sub.length);
-            currentChar += skip;
-            currentDollar += skip;
-        } else {
-            currentDollar++;
-        }
-
-        currentDollar = js_strchr_limit(currentDollar, '$', replacementEnd);
-    } while (currentDollar);
-    sb.infallibleAppend(currentChar, replacement->length() - (currentChar - replacementBegin));
-}
-
-/* ES 2016 draft Mar 25, 2016 21.1.3.14.1. */
-bool
-js::RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinearString string,
-                          size_t position, HandleObject capturesObj, HandleLinearString replacement,
-                          size_t firstDollarIndex, MutableHandleValue rval)
-{
-    MOZ_ASSERT(firstDollarIndex < replacement->length());
-
-    // Step 1 (skipped).
-
-    // Step 2.
-    size_t matchLength = matched->length();
-
-    // Steps 3-5 (skipped).
-
-    // Step 6.
-    MOZ_ASSERT(position <= string->length());
-
-    // Step 10 (reordered).
-    uint32_t nCaptures;
-    if (!GetLengthProperty(cx, capturesObj, &nCaptures))
-        return false;
-
-    AutoValueVector captures(cx);
-    if (!captures.reserve(nCaptures))
-        return false;
-
-    // Step 7.
-    RootedValue capture(cx);
-    for (uint32_t i = 0; i < nCaptures; i++) {
-        if (!GetElement(cx, capturesObj, capturesObj, i, &capture))
-            return false;
-
-        if (capture.isUndefined()) {
-            captures.infallibleAppend(capture);
-            continue;
-        }
-
-        MOZ_ASSERT(capture.isString());
-        RootedLinearString captureLinear(cx, capture.toString()->ensureLinear(cx));
-        if (!captureLinear)
-            return false;
-        captures.infallibleAppend(StringValue(captureLinear));
-    }
-
-    // Step 8 (skipped).
-
-    // Step 9.
-    CheckedInt<uint32_t> checkedTailPos(0);
-    checkedTailPos += position;
-    checkedTailPos += matchLength;
-    if (!checkedTailPos.isValid()) {
-        ReportAllocationOverflow(cx);
-        return false;
-    }
-    uint32_t tailPos = checkedTailPos.value();
-
-    // Step 11.
-    size_t reserveLength;
-    if (!FindReplaceLength(cx, matched, string, position, tailPos, captures, replacement,
-                           firstDollarIndex, &reserveLength))
-    {
-        return false;
-    }
-
-    StringBuffer result(cx);
-    if (string->hasTwoByteChars() || replacement->hasTwoByteChars()) {
-        if (!result.ensureTwoByteChars())
-            return false;
-    }
-
-    if (!result.reserve(reserveLength))
-        return false;
-
-    if (replacement->hasLatin1Chars()) {
-        DoReplace<Latin1Char>(matched, string, position, tailPos, captures,
-                              replacement, firstDollarIndex, result);
-    } else {
-        DoReplace<char16_t>(matched, string, position, tailPos, captures,
-                            replacement, firstDollarIndex, result);
-    }
-
-    // Step 12.
-    JSString* resultString = result.finishString();
-    if (!resultString)
-        return false;
-
-    rval.setString(resultString);
-    return true;
-}
-
-bool
-js::RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp)
-{
-    // This can only be called from self-hosted code.
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-
-    uint8_t result = false;
-    if (!RegExpPrototypeOptimizableRaw(cx, &args[0].toObject(), &result))
-        return false;
-
-    args.rval().setBoolean(result);
-    return true;
-}
-
-bool
-js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* result)
-{
-    JS::AutoCheckCannotGC nogc;
-    if (!proto->isNative()) {
-        *result = false;
-        return true;
-    }
-
-    NativeObject* nproto = static_cast<NativeObject*>(proto);
-
-    Shape* shape = cx->compartment()->regExps.getOptimizableRegExpPrototypeShape();
-    if (shape == nproto->lastProperty()) {
-        *result = true;
-        return true;
-    }
-
-    JSNative globalGetter;
-    if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().global), &globalGetter))
-        return false;
-
-    if (globalGetter != regexp_global) {
-        *result = false;
-        return true;
-    }
-
-    JSNative stickyGetter;
-    if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().sticky), &stickyGetter))
-        return false;
-
-    if (stickyGetter != regexp_sticky) {
-        *result = false;
-        return true;
-    }
-
-    // Check if @@match, @@search, and exec are own data properties,
-    // those values should be tested in selfhosted JS.
-    bool has = false;
-    if (!HasOwnDataPropertyPure(cx, proto, SYMBOL_TO_JSID(cx->wellKnownSymbols().match), &has))
-        return false;
-    if (!has) {
-        *result = false;
-        return true;
-    }
-
-    if (!HasOwnDataPropertyPure(cx, proto, SYMBOL_TO_JSID(cx->wellKnownSymbols().search), &has))
-        return false;
-    if (!has) {
-        *result = false;
-        return true;
-    }
-
-    if (!HasOwnDataPropertyPure(cx, proto, NameToId(cx->names().exec), &has))
-        return false;
-    if (!has) {
-        *result = false;
-        return true;
-    }
-
-    cx->compartment()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
-    *result = true;
-    return true;
-}
-
-bool
-js::RegExpInstanceOptimizable(JSContext* cx, unsigned argc, Value* vp)
-{
-    // This can only be called from self-hosted code.
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-
-    uint8_t result = false;
-    if (!RegExpInstanceOptimizableRaw(cx, &args[0].toObject(), &args[1].toObject(), &result))
-        return false;
-
-    args.rval().setBoolean(result);
-    return true;
-}
-
-bool
-js::RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* rx, JSObject* proto, uint8_t* result)
-{
-    JS::AutoCheckCannotGC nogc;
-    if (!rx->isNative()) {
-        *result = false;
-        return true;
-    }
-
-    NativeObject* nobj = static_cast<NativeObject*>(rx);
-
-    Shape* shape = cx->compartment()->regExps.getOptimizableRegExpInstanceShape();
-    if (shape == nobj->lastProperty()) {
-        *result = true;
-        return true;
-    }
-
-    if (rx->hasLazyPrototype()) {
-        *result = false;
-        return true;
-    }
-
-    if (rx->getTaggedProto().toObjectOrNull() != proto) {
-        *result = false;
-        return true;
-    }
-
-    if (!RegExpObject::isInitialShape(nobj)) {
-        *result = false;
-        return true;
-    }
-
-    cx->compartment()->regExps.setOptimizableRegExpInstanceShape(nobj->lastProperty());
-    *result = true;
-    return true;
-}
-
-/*
- * Pattern match the script to check if it is is indexing into a particular
- * object, e.g. 'function(a) { return b[a]; }'. Avoid calling the script in
- * such cases, which are used by javascript packers (particularly the popular
- * Dean Edwards packer) to efficiently encode large scripts. We only handle the
- * code patterns generated by such packers here.
- */
-bool
-js::intrinsic_GetElemBaseForLambda(JSContext* cx, unsigned argc, Value* vp)
-{
-    // This can only be called from self-hosted code.
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 1);
-
-    JSObject& lambda = args[0].toObject();
-    args.rval().setUndefined();
-
-    if (!lambda.is<JSFunction>())
-        return true;
-
-    RootedFunction fun(cx, &lambda.as<JSFunction>());
-    if (!fun->isInterpreted() || fun->isClassConstructor())
-        return true;
-
-    JSScript* script = fun->getOrCreateScript(cx);
-    if (!script)
-        return false;
-
-    jsbytecode* pc = script->code();
-
-    /*
-     * JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
-     * Rule out the (unlikely) possibility of a function with a call object
-     * since it would make our scope walk off by 1.
-     */
-    if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->needsCallObject())
-        return true;
-    ScopeCoordinate sc(pc);
-    ScopeObject* scope = &fun->environment()->as<ScopeObject>();
-    for (unsigned i = 0; i < sc.hops(); ++i)
-        scope = &scope->enclosingScope().as<ScopeObject>();
-    Value b = scope->aliasedVar(sc);
-    pc += JSOP_GETALIASEDVAR_LENGTH;
-
-    /* Look for 'a' to be the lambda's first argument. */
-    if (JSOp(*pc) != JSOP_GETARG || GET_ARGNO(pc) != 0)
-        return true;
-    pc += JSOP_GETARG_LENGTH;
-
-    /* 'b[a]' */
-    if (JSOp(*pc) != JSOP_GETELEM)
-        return true;
-    pc += JSOP_GETELEM_LENGTH;
-
-    /* 'return b[a]' */
-    if (JSOp(*pc) != JSOP_RETURN)
-        return true;
-
-    /* 'b' must behave like a normal object. */
-    if (!b.isObject())
-        return true;
-
-    JSObject& bobj = b.toObject();
-    const Class* clasp = bobj.getClass();
-    if (!clasp->isNative() || clasp->getOpsLookupProperty() || clasp->getOpsGetProperty())
-        return true;
-
-    args.rval().setObject(bobj);
-    return true;
-}
-
-/*
- * Emulates `b[a]` property access, that is detected in GetElemBaseForLambda.
- * It returns the property value only if the property is data property and the
- * propety value is a string.  Otherwise it returns undefined.
- */
-bool
-js::intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-
-    RootedObject obj(cx, &args[0].toObject());
-    if (!obj->isNative()) {
-        // The object is already checked to be native in GetElemBaseForLambda,
-        // but it can be swapped to the other class that is non-native.
-        // Return undefined to mark failure to get the property.
-        args.rval().setUndefined();
-        return true;
-    }
-
-    RootedNativeObject nobj(cx, &obj->as<NativeObject>());
-    RootedString name(cx, args[1].toString());
-
-    RootedAtom atom(cx, AtomizeString(cx, name));
-    if (!atom)
-        return false;
-
-    RootedValue v(cx);
-    if (HasDataProperty(cx, nobj, AtomToId(atom), v.address()) && v.isString())
-        args.rval().set(v);
-    else
-        args.rval().setUndefined();
-
-    return true;
-}
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -40,37 +40,25 @@ bool
 CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                         MutableHandleValue rval);
 
 extern bool
 RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                 int32_t lastIndex, MatchPairs* maybeMatches, MutableHandleValue output);
-
-extern bool
-RegExpSearcher(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool
-RegExpSearcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                  int32_t lastIndex, MatchPairs* maybeMatches, int32_t* result);
+                 int32_t lastIndex, bool sticky,
+                 MatchPairs* maybeMatches, MutableHandleValue output);
 
 extern bool
 RegExpTester(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
-                int32_t lastIndex, int32_t* endIndex);
-
-extern bool
-intrinsic_GetElemBaseForLambda(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool
-intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp);
+                int32_t lastIndex, bool sticky, int32_t* endIndex);
 
 /*
  * The following functions are for use by self-hosted code.
  */
 
 /*
  * Behaves like regexp.exec(string), but doesn't set RegExp statics.
  *
@@ -92,49 +80,19 @@ regexp_test_no_statics(JSContext* cx, un
  * pattern and flags should be string, and should be called without |new|.
  *
  * Usage: re = regexp_construct(pattern)
  *        re = regexp_construct(pattern, flags)
  */
 extern bool
 regexp_construct_self_hosting(JSContext* cx, unsigned argc, Value* vp);
 
-/*
- * Behaves like RegExp(pattern, string).
- * pattern should be a RegExp object, and flags should be a string,
- * and should be called without |new|.
- * Dedicated function for RegExp.prototype.split optimized path.
- * sticky flag is ignored.
- */
-extern bool
-regexp_construct_no_sticky(JSContext* cx, unsigned argc, Value* vp);
-
 extern bool
 IsRegExp(JSContext* cx, HandleValue value, bool* result);
 
-extern bool
-RegExpCreate(JSContext* cx, HandleValue pattern, HandleValue flags, MutableHandleValue rval);
-
-extern bool
-RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool
-RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* result);
-
-extern bool
-RegExpInstanceOptimizable(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool
-RegExpInstanceOptimizableRaw(JSContext* cx, JSObject* rx, JSObject* proto, uint8_t* result);
-
-extern bool
-RegExpGetSubstitution(JSContext* cx, HandleLinearString matched, HandleLinearString string,
-                      size_t position, HandleObject capturesObj, HandleLinearString replacement,
-                      size_t firstDollarIndex, MutableHandleValue rval);
-
 // RegExp ClassSpec members used in RegExpObject.cpp.
 extern bool
 regexp_construct(JSContext* cx, unsigned argc, Value* vp);
 extern const JSPropertySpec regexp_static_props[];
 extern const JSPropertySpec regexp_properties[];
 extern const JSFunctionSpec regexp_methods[];
 
 // Used in RegExpObject::isOriginalFlagGetter.
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -51,716 +51,16 @@ function RegExpToString()
     // Steps 5-6.
     var flags = R.flags;
 
     // Step 7.
     return '/' + pattern + '/' + flags;
 }
 _SetCanonicalName(RegExpToString, "toString");
 
-// ES 2016 draft Mar 25, 2016 21.2.5.2.3.
-function AdvanceStringIndex(S, index) {
-    // Step 1.
-    assert(typeof S === "string", "Expected string as 1st argument");
-
-    // Step 2.
-    assert(index >= 0 && index <= MAX_NUMERIC_INDEX, "Expected integer as 2nd argument");
-
-    // Step 3 (skipped).
-
-    // Step 4 (skipped).
-
-    // Step 5.
-    var length = S.length;
-
-    // Step 6.
-    if (index + 1 >= length)
-        return index + 1;
-
-    // Step 7.
-    var first = callFunction(std_String_charCodeAt, S, index);
-
-    // Step 8.
-    if (first < 0xD800 || first > 0xDBFF)
-        return index + 1;
-
-    // Step 9.
-    var second = callFunction(std_String_charCodeAt, S, index + 1);
-
-    // Step 10.
-    if (second < 0xDC00 || second > 0xDFFF)
-        return index + 1;
-
-    // Step 11.
-    return index + 2;
-}
-
-// ES 2016 draft Mar 25, 2016 21.2.5.6.
-function RegExpMatch(string) {
-    // Step 1.
-    var rx = this;
-
-    // Step 2.
-    if (!IsObject(rx))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx);
-
-    // Step 3.
-    var S = ToString(string);
-
-    // Steps 4-5.
-    if (!rx.global)
-        return RegExpExec(rx, S, false);
-
-    // Step 6.a.
-    var fullUnicode = !!rx.unicode;
-
-    // Step 6.b.
-    rx.lastIndex = 0;
-
-    // Step 6.c.
-    var A = [];
-
-    // Step 6.d.
-    var n = 0;
-
-    // Step 6.e.
-    while (true) {
-        // Step 6.e.i.
-        var result = RegExpExec(rx, S, false);
-
-        // Step 6.e.ii.
-        if (result === null)
-          return (n === 0) ? null : A;
-
-        // Step 6.e.iii.1.
-        var matchStr = ToString(result[0]);
-
-        // Step 6.e.iii.2.
-        _DefineDataProperty(A, n, matchStr);
-
-        // Step 6.e.iii.4.
-        if (matchStr === "") {
-            var lastIndex = ToLength(rx.lastIndex);
-            rx.lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
-        }
-
-        // Step 6.e.iii.5.
-        n++;
-    }
-}
-
-// Checks if following properties and getters are not modified, and accessing
-// them not observed by content script:
-//   * global
-//   * sticky
-//   * exec
-//   * lastIndex
-function IsRegExpMethodOptimizable(rx) {
-    var RegExpProto = GetBuiltinPrototype("RegExp");
-    // If RegExpPrototypeOptimizable and RegExpInstanceOptimizable succeed,
-    // `RegExpProto.exec` is guaranteed to be data properties.
-    return RegExpPrototypeOptimizable(RegExpProto) &&
-           RegExpInstanceOptimizable(rx, RegExpProto) &&
-           RegExpProto.exec === RegExp_prototype_Exec;
-}
-
-// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8.
-function RegExpReplace(string, replaceValue) {
-    // Step 1.
-    var rx = this;
-
-    // Step 2.
-    if (!IsObject(rx))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx);
-
-    // Step 3.
-    var S = ToString(string);
-
-    // Step 4.
-    var lengthS = S.length;
-
-    // Step 5.
-    var functionalReplace = IsCallable(replaceValue);
-
-    // Step 6.
-    var firstDollarIndex = -1;
-    if (!functionalReplace) {
-        // Step 6.a.
-        replaceValue = ToString(replaceValue);
-
-        // Skip if replaceValue is an empty string or a single character.
-        // A single character string may contain "$", but that cannot be a
-        // substitution.
-        if (replaceValue.length > 1)
-            firstDollarIndex = callFunction(std_String_indexOf, replaceValue, "$");
-    }
-
-    // Step 7.
-    var global = !!rx.global;
-
-    // Optimized paths.
-    if (IsRegExpMethodOptimizable(rx)) {
-        // Steps 8-16.
-        if (global) {
-            if (functionalReplace) {
-                var elemBase = GetElemBaseForLambda(replaceValue);
-                if (IsObject(elemBase))
-                    return RegExpGlobalReplaceOptElemBase(rx, S, lengthS, replaceValue, elemBase);
-                return RegExpGlobalReplaceOptFunc(rx, S, lengthS, replaceValue);
-            }
-            if (firstDollarIndex !== -1)
-                return RegExpGlobalReplaceOptSubst(rx, S, lengthS, replaceValue, firstDollarIndex);
-            if (lengthS < 0x7fff)
-                return RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue);
-            return RegExpGlobalReplaceOpt(rx, S, lengthS, replaceValue);
-        }
-
-        if (functionalReplace)
-            return RegExpLocalReplaceOptFunc(rx, S, lengthS, replaceValue);
-        if (firstDollarIndex !== -1)
-            return RegExpLocalReplaceOptSubst(rx, S, lengthS, replaceValue, firstDollarIndex);
-        return RegExpLocalReplaceOpt(rx, S, lengthS, replaceValue);
-    }
-
-    // Steps 8-16.
-    return RegExpReplaceSlowPath(rx, S, lengthS, replaceValue,
-                                 functionalReplace, firstDollarIndex, global);
-}
-
-// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
-// steps 8-16.
-// Slow path for @@replace.
-function RegExpReplaceSlowPath(rx, S, lengthS, replaceValue,
-                               functionalReplace, firstDollarIndex, global)
-{
-    // Step 8.
-    var fullUnicode = false;
-    if (global) {
-        // Step 8.a.
-        fullUnicode = !!rx.unicode;
-
-        // Step 8.b.
-        rx.lastIndex = 0;
-    }
-
-    // Step 9.
-    var results = [];
-    var nResults = 0;
-
-    // Step 11.
-    while (true) {
-        // Step 11.a.
-        var result = RegExpExec(rx, S, false);
-
-        // Step 11.b.
-        if (result === null)
-            break;
-
-        // Step 11.c.i.
-        _DefineDataProperty(results, nResults++, result);
-
-        // Step 11.c.ii.
-        if (!global)
-            break;
-
-        // Step 11.c.iii.1.
-        var matchStr = ToString(result[0]);
-
-        // Step 11.c.iii.2.
-        if (matchStr === "") {
-            var lastIndex = ToLength(rx.lastIndex);
-            rx.lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
-        }
-    }
-
-    // Step 12.
-    var accumulatedResult = "";
-
-    // Step 13.
-    var nextSourcePosition = 0;
-
-    // Step 14.
-    for (var i = 0; i < nResults; i++) {
-        result = results[i];
-
-        // Steps 14.a-b.
-        var nCaptures = std_Math_max(ToLength(result.length) - 1, 0);
-
-        // Step 14.c.
-        var matched = ToString(result[0]);
-
-        // Step 14.d.
-        var matchLength = matched.length;
-
-        // Steps 14.e-f.
-        var position = std_Math_max(std_Math_min(ToInteger(result.index), lengthS), 0);
-
-        var n, capN, replacement;
-        if (functionalReplace || firstDollarIndex !== -1) {
-            // Steps 14.g-j.
-            replacement = RegExpGetComplexReplacement(result, matched, S, position,
-
-                                                      nCaptures, replaceValue,
-                                                      functionalReplace, firstDollarIndex);
-        } else {
-            // Step 14.g, 14.i, 14.i.iv.
-            // We don't need captures array, but ToString is visible to script.
-            for (n = 1; n <= nCaptures; n++) {
-                // Step 14.i.i-ii.
-                capN = result[n];
-
-                // Step 14.i.ii.
-                if (capN !== undefined)
-                    ToString(capN);
-            }
-            replacement = replaceValue;
-        }
-
-        // Step 14.l.
-        if (position >= nextSourcePosition) {
-            // Step 14.l.ii.
-          accumulatedResult += Substring(S, nextSourcePosition,
-                                         position - nextSourcePosition) + replacement;
-
-            // Step 14.l.iii.
-            nextSourcePosition = position + matchLength;
-        }
-    }
-
-    // Step 15.
-    if (nextSourcePosition >= lengthS)
-        return accumulatedResult;
-
-    // Step 16.
-    return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition);
-}
-
-// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
-// steps 14.g-k.
-// Calculates functional/substitution replaceement from match result.
-// Used in the following functions:
-//   * RegExpGlobalReplaceOptFunc
-//   * RegExpGlobalReplaceOptElemBase
-//   * RegExpGlobalReplaceOptSubst
-//   * RegExpLocalReplaceOptFunc
-//   * RegExpLocalReplaceOptSubst
-//   * RegExpReplaceSlowPath
-function RegExpGetComplexReplacement(result, matched, S, position,
-                                     nCaptures, replaceValue,
-                                     functionalReplace, firstDollarIndex)
-{
-    // Step 14.h.
-    var captures = [];
-    var capturesLength = 0;
-
-    // Step 14.j.i (reordered).
-    // For `nCaptures` <= 4 case, call `replaceValue` directly, otherwise
-    // use `std_Function_apply` with all arguments stored in `captures`.
-    // In latter case, store `matched` as the first element here, to
-    // avoid unshift later.
-    if (functionalReplace && nCaptures > 4)
-        _DefineDataProperty(captures, capturesLength++, matched);
-
-    // Step 14.g, 14.i, 14.i.iv.
-    for (var n = 1; n <= nCaptures; n++) {
-        // Step 14.i.i.
-        var capN = result[n];
-
-        // Step 14.i.ii.
-        if (capN !== undefined)
-            capN = ToString(capN);
-
-        // Step 14.i.iii.
-        _DefineDataProperty(captures, capturesLength++, capN);
-    }
-
-    // Step 14.j.
-    if (functionalReplace) {
-        switch (nCaptures) {
-          case 0:
-            return ToString(replaceValue(matched, position, S));
-         case 1:
-            return ToString(replaceValue(matched, captures[0], position, S));
-          case 2:
-            return ToString(replaceValue(matched, captures[0], captures[1],
-                                         position, S));
-          case 3:
-            return ToString(replaceValue(matched, captures[0], captures[1],
-                                         captures[2], position, S));
-          case 4:
-            return  ToString(replaceValue(matched, captures[0], captures[1],
-                                          captures[2], captures[3], position, S));
-          default:
-            // Steps 14.j.ii-v.
-            _DefineDataProperty(captures, capturesLength++, position);
-            _DefineDataProperty(captures, capturesLength++, S);
-            return ToString(callFunction(std_Function_apply, replaceValue, null, captures));
-        }
-    }
-
-    // Steps 14.k.i.
-    return RegExpGetSubstitution(matched, S, position, captures, replaceValue,
-                                 firstDollarIndex);
-}
-
-// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
-// steps 8-16.
-// Optimized path for @@replace with the following conditions:
-//   * global flag is true
-//   * S is a short string (lengthS < 0x7fff)
-//   * replaceValue is a string without "$"
-function RegExpGlobalReplaceShortOpt(rx, S, lengthS, replaceValue)
-{
-    // Step 8.a.
-    var fullUnicode = !!rx.unicode;
-
-    // Step 8.b.
-    var lastIndex = 0;
-    rx.lastIndex = 0;
-
-    // Step 12 (reordered).
-    var accumulatedResult = "";
-
-    // Step 13 (reordered).
-    var nextSourcePosition = 0;
-
-    // Step 11.
-    while (true) {
-        // Step 11.a.
-        var result = RegExpSearcher(rx, S, lastIndex);
-
-        // Step 11.b.
-        if (result === -1)
-            break;
-
-        var position = result & 0x7fff;
-        lastIndex = (result >> 15) & 0x7fff;
-
-        // Step 14.l.ii.
-        accumulatedResult += Substring(S, nextSourcePosition,
-                                       position - nextSourcePosition) + replaceValue;
-
-        // Step 14.l.iii.
-        nextSourcePosition = lastIndex;
-
-        // Step 11.c.iii.2.
-        if (lastIndex === position) {
-            lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
-            if (lastIndex > lengthS)
-                break;
-        }
-    }
-
-    // Step 15.
-    if (nextSourcePosition >= lengthS)
-        return accumulatedResult;
-
-    // Step 16.
-    return accumulatedResult + Substring(S, nextSourcePosition, lengthS - nextSourcePosition);
-}
-
-// ES 2017 draft rev 03bfda119d060aca4099d2b77cf43f6d4f11cfa2 21.2.5.8
-// steps 8-16.
-// Optimized path for @@replace.
-
-// Conditions:
-//   * global flag is true
-//   * replaceValue is a string without "$"
-#define FUNC_NAME RegExpGlobalReplaceOpt
-#include "RegExpGlobalReplaceOpt.h.js"
-#undef FUNC_NAME
-
-// Conditions:
-//   * global flag is true
-//   * replaceValue is a function
-#define FUNC_NAME RegExpGlobalReplaceOptFunc
-#define FUNCTIONAL
-#include "RegExpGlobalReplaceOpt.h.js"
-#undef FUNCTIONAL
-#undef FUNC_NAME
-
-// Conditions:
-//   * global flag is true
-//   * replaceValue is a function that returns element of an object
-#define FUNC_NAME RegExpGlobalReplaceOptElemBase
-#define ELEMBASE
-#include "RegExpGlobalReplaceOpt.h.js"
-#undef ELEMBASE
-#undef FUNC_NAME
-
-// Conditions:
-//   * global flag is true
-//   * replaceValue is a string with "$"
-#define FUNC_NAME RegExpGlobalReplaceOptSubst
-#define SUBSTITUTION
-#include "RegExpGlobalReplaceOpt.h.js"
-#undef SUBSTITUTION
-#undef FUNC_NAME
-
-// Conditions:
-//   * global flag is false
-//   * replaceValue is a string without "$"
-#define FUNC_NAME RegExpLocalReplaceOpt
-#include "RegExpLocalReplaceOpt.h.js"
-#undef FUNC_NAME
-
-// Conditions:
-//   * global flag is false
-//   * replaceValue is a function
-#define FUNC_NAME RegExpLocalReplaceOptFunc
-#define FUNCTIONAL
-#include "RegExpLocalReplaceOpt.h.js"
-#undef FUNCTIONAL
-#undef FUNC_NAME
-
-// Conditions:
-//   * global flag is false
-//   * replaceValue is a string with "$"
-#define FUNC_NAME RegExpLocalReplaceOptSubst
-#define SUBSTITUTION
-#include "RegExpLocalReplaceOpt.h.js"
-#undef SUBSTITUTION
-#undef FUNC_NAME
-
-// ES 2016 draft Mar 25, 2016 21.2.5.9.
-function RegExpSearch(string) {
-    // Step 1.
-    var rx = this;
-
-    // Step 2.
-    if (!IsObject(rx))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx);
-
-    // Step 3.
-    var S = ToString(string);
-
-    var result;
-    if (IsRegExpMethodOptimizable(rx) && S.length < 0x7fff) {
-        // Step 6.
-        result = RegExpSearcher(rx, S, 0);
-
-        // Step 8.
-        if (result === -1)
-            return -1;
-
-        // Step 9.
-        return result & 0x7fff;
-    }
-
-    // Step 4.
-    var previousLastIndex = rx.lastIndex;
-
-    // Step 5.
-    rx.lastIndex = 0;
-
-    // Step 6.
-    result = RegExpExec(rx, S, false);
-
-    // Step 7.
-    rx.lastIndex = previousLastIndex;
-
-    // Step 8.
-    if (result === null)
-        return -1;
-
-    // Step 9.
-    return result.index;
-}
-
-function IsRegExpSplitOptimizable(rx, C) {
-    if (!IsRegExpObject(rx))
-        return false;
-
-    var RegExpCtor = GetBuiltinConstructor("RegExp");
-    if (C !== RegExpCtor)
-        return false;
-
-    var RegExpProto = RegExpCtor.prototype;
-    // If RegExpPrototypeOptimizable succeeds, `RegExpProto.exec` is guaranteed
-    // to be a data property.
-    return RegExpPrototypeOptimizable(RegExpProto) &&
-           RegExpProto.exec === RegExp_prototype_Exec;
-}
-
-// ES 2016 draft Mar 25, 2016 21.2.5.11.
-function RegExpSplit(string, limit) {
-    // Step 1.
-    var rx = this;
-
-    // Step 2.
-    if (!IsObject(rx))
-        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx);
-
-    // Step 3.
-    var S = ToString(string);
-
-    // Step 4.
-    var C = SpeciesConstructor(rx, GetBuiltinConstructor("RegExp"));
-
-    // Step 5.
-    var flags = ToString(rx.flags);
-
-    // Steps 6-7.
-    var unicodeMatching = callFunction(std_String_includes, flags, "u");
-
-    // Step 14 (reordered).
-    var size = S.length;
-
-    var optimizable = IsRegExpSplitOptimizable(rx, C);
-    var splitter;
-    if (optimizable && size !== 0) {
-        // Steps 8-9 (skipped).
-
-        // Step 10.
-        // If split operation is optimizable, perform non-sticky match.
-        splitter = regexp_construct_no_sticky(rx, flags);
-    } else {
-        // Steps 8-9.
-        var newFlags;
-        if (callFunction(std_String_includes, flags, "y"))
-            newFlags = flags;
-        else
-            newFlags = flags + "y";
-
-        // Step 10.
-        splitter = new C(rx, newFlags);
-    }
-
-    // Step 11.
-    var A = [];
-
-    // Step 12.
-    var lengthA = 0;
-
-    // Step 13.
-    var lim;
-    if (limit === undefined)
-        lim = MAX_NUMERIC_INDEX;
-    else
-        lim = limit >>> 0;
-
-    // Step 16;
-    var p = 0;
-
-    // Step 16;
-    if (lim === 0)
-        return A;
-
-    // Step 17.
-    if (size === 0) {
-        // Step 17.a.
-        var z = RegExpExec(splitter, S, false);
-
-        // Step 17.b.
-        if (z !== null)
-            return A;
-
-        // Step 17.d.
-        _DefineDataProperty(A, 0, S);
-
-        // Step 17.e.
-        return A;
-    }
-
-    // Step 18.
-    var q = p;
-
-    // Step 19.
-    while (q < size) {
-        var e;
-        if (optimizable) {
-            // Step 19.a (skipped).
-            // splitter.lastIndex is not used.
-
-            // Step 19.b.
-            z = RegExpMatcher(splitter, S, q);
-
-            // Step 19.c.
-            if (z === null)
-                break;
-
-            // splitter.lastIndex is not updated.
-            q = z.index;
-            if (q >= size)
-                break;
-
-            // Step 19.d.i.
-            e = ToLength(q + z[0].length);
-        } else {
-            // Step 19.a.
-            splitter.lastIndex = q;
-
-            // Step 19.b.
-            z = RegExpExec(splitter, S, false);
-
-            // Step 19.c.
-            if (z === null) {
-                q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1;
-                continue;
-            }
-
-            // Step 19.d.i.
-            e = ToLength(splitter.lastIndex);
-        }
-
-        // Step 19.d.iii.
-        if (e === p) {
-            q = unicodeMatching ? AdvanceStringIndex(S, q) : q + 1;
-            continue;
-        }
-
-        // Steps 19.d.iv.1-3.
-        _DefineDataProperty(A, lengthA, Substring(S, p, q - p));
-
-        // Step 19.d.iv.4.
-        lengthA++;
-
-        // Step 19.d.iv.5.
-        if (lengthA === lim)
-            return A;
-
-        // Step 19.d.iv.6.
-        p = e;
-
-        // Steps 19.d.iv.7-8.
-        var numberOfCaptures = std_Math_max(ToLength(z.length) - 1, 0);
-
-        // Step 19.d.iv.9.
-        var i = 1;
-
-        // Step 19.d.iv.10.
-        while (i <= numberOfCaptures) {
-            // Steps 19.d.iv.10.a-b.
-            _DefineDataProperty(A, lengthA, z[i]);
-
-            // Step 19.d.iv.10.c.
-            i++;
-
-            // Step 19.d.iv.10.d.
-            lengthA++;
-
-            // Step 19.d.iv.10.e.
-            if (lengthA === lim)
-                return A;
-        }
-
-        // Step 19.d.iv.11.
-        q = p;
-    }
-
-    // Steps 20-22.
-    if (p >= size)
-        _DefineDataProperty(A, lengthA, "");
-    else
-        _DefineDataProperty(A, lengthA, Substring(S, p, size - p));
-
-    // Step 23.
-    return A;
-}
-
 // ES6 21.2.5.2.
 // NOTE: This is not RegExpExec (21.2.5.2.1).
 function RegExp_prototype_Exec(string) {
     // Steps 1-3.
     var R = this;
     if (!IsObject(R) || !IsRegExpObject(R))
         return callFunction(CallRegExpMethodIfWrapped, R, string, "RegExp_prototype_Exec");
 
@@ -794,70 +94,70 @@ function RegExpExec(R, S, forTest) {
     // Step 5.c.
     if (typeof result !== "object")
         ThrowTypeError(JSMSG_EXEC_NOT_OBJORNULL);
 
     // Step 5.d.
     return forTest ? result !== null : result;
 }
 
-// ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2.
+// ES6 21.2.5.2.2.
 function RegExpBuiltinExec(R, S, forTest) {
     // ES6 21.2.5.2.1 step 6.
     // This check is here for RegExpTest.  RegExp_prototype_Exec does same
     // thing already.
     if (!IsRegExpObject(R))
         return callFunction(CallRegExpMethodIfWrapped, R, R, S, forTest, "RegExpBuiltinExec");
 
-    // Steps 1-2 (skipped).
+    // Step 1-2 (skipped).
 
-    // Step 4.
+    // Step 4-5.
     var lastIndex = ToLength(R.lastIndex);
 
-    // Step 5.
-    var flags = UnsafeGetInt32FromReservedSlot(R, REGEXP_FLAGS_SLOT);
+    // Steps 6-7.
+    var global = !!R.global;
 
-    // Steps 6-7.
-    var globalOrSticky = !!(flags & (REGEXP_GLOBAL_FLAG | REGEXP_STICKY_FLAG));
+    // Steps 8-9.
+    var sticky = !!R.sticky;
 
-    // Step 8.
-    if (!globalOrSticky) {
+    // Step 10.
+    if (!global && !sticky) {
         lastIndex = 0;
     } else {
         if (lastIndex > S.length) {
-            // Steps 12.a.i-ii, 12.c.i.1-2.
+            // Steps 15.a.i-ii, 15.c.i.1-2.
             R.lastIndex = 0;
             return forTest ? false : null;
         }
     }
 
     if (forTest) {
-        // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15.
-        var endIndex = RegExpTester(R, S, lastIndex);
+        // Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
+        var endIndex = RegExpTester(R, S, lastIndex, sticky);
         if (endIndex == -1) {
-            // Steps 12.a.i-ii, 12.c.i.1-2.
+            // Steps 15.a.i-ii, 15.c.i.1-2.
             R.lastIndex = 0;
             return false;
         }
 
-        // Step 15.
-        if (globalOrSticky)
+        // Step 18.
+        if (global || sticky)
             R.lastIndex = endIndex;
 
         return true;
     }
 
-    // Steps 3, 9-25, except 12.a.i-ii, 12.c.i.1-2, 15.
-    var result = RegExpMatcher(R, S, lastIndex);
+    // Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
+    var result = RegExpMatcher(R, S, lastIndex, sticky);
     if (result === null) {
-        // Steps 12.a.i-ii, 12.c.i.1-2.
+        // Steps 15.a.i-ii, 15.c.i.1-2.
         R.lastIndex = 0;
     } else {
-        // Step 15.
-        if (globalOrSticky)
+        // Step 18.
+        if (global || sticky)
             R.lastIndex = result.index + result[0].length;
     }
 
     return result;
 }
 
 // ES6 21.2.5.13.
 function RegExpTest(string) {
@@ -867,14 +167,8 @@ function RegExpTest(string) {
         ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R);
 
     // Steps 3-4.
     var S = ToString(string);
 
     // Steps 5-6.
     return RegExpExec(R, S, true);
 }
-
-// ES 2016 draft Mar 25, 2016 21.2.4.2.
-function RegExpSpecies() {
-    // Step 1.
-    return this;
-}
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -79,17 +79,9 @@
 // NB: keep these in sync with the copy in jsfriendapi.h.
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
 #define JSITER_SYMBOLS    0x20  /* also include symbol property keys */
 #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
 
 #define TELEMETRY_DEFINE_GETTER_SETTER_THIS_NULL_UNDEFINED 25
 
-#define REGEXP_FLAGS_SLOT 2
-
-#define REGEXP_IGNORECASE_FLAG  0x01
-#define REGEXP_GLOBAL_FLAG      0x02
-#define REGEXP_MULTILINE_FLAG   0x04
-#define REGEXP_STICKY_FLAG      0x08
-#define REGEXP_UNICODE_FLAG     0x10
-
 #endif
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -1,81 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*global intl_Collator: false, */
 
-function StringProtoHasNoMatch() {
-    var ObjectProto = GetBuiltinPrototype("Object");
-    var StringProto = GetBuiltinPrototype("String");
-    if (!ObjectHasPrototype(StringProto, ObjectProto))
-        return false;
-    return !(std_match in StringProto);
-}
-
-function IsStringMatchOptimizable() {
-    var RegExpProto = GetBuiltinPrototype("RegExp");
-    // If RegExpPrototypeOptimizable succeeds, `exec` and `@@match` are
-    // guaranteed to be data properties.
-    return RegExpPrototypeOptimizable(RegExpProto) &&
-           RegExpProto.exec === RegExp_prototype_Exec &&
-           RegExpProto[std_match] === RegExpMatch;
-}
-
-// ES 2016 draft Mar 25, 2016 21.1.3.11.
-function String_match(regexp) {
-    // Step 1.
-    RequireObjectCoercible(this);
-
-    // Step 2.
-    var isPatternString = (typeof regexp === "string");
-    if (!(isPatternString && StringProtoHasNoMatch()) && regexp !== undefined && regexp !== null) {
-        // Step 2.a.
-        var matcher = GetMethod(regexp, std_match);
-
-        // Step 2.b.
-        if (matcher !== undefined)
-            return callContentFunction(matcher, regexp, this);
-    }
-
-    // Step 3.
-    var S = ToString(this);
-
-    // FIXME: Non-standard flags argument (bug 1108382).
-    var flags = undefined;
-    if (arguments.length > 1) {
-        if (IsMatchFlagsArgumentEnabled())
-            flags = ToString(arguments[1]);
-        WarnOnceAboutFlagsArgument();
-    } else {
-        if (isPatternString && IsStringMatchOptimizable()) {
-            var flatResult = FlatStringMatch(S, regexp);
-            if (flatResult !== undefined)
-                return flatResult;
-        }
-    }
-
-    // Step 4.
-    var rx = RegExpCreate(regexp, flags);
-
-    // Step 5 (optimized case).
-    if (IsStringMatchOptimizable() && !flags)
-        return RegExpMatcher(rx, S, 0);
-
-    // Step 5.
-    return callContentFunction(GetMethod(rx, std_match), rx, S);
-}
-
-function String_generic_match(thisValue, regexp) {
-    if (thisValue === undefined)
-        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.match');
-    return callFunction(String_match, thisValue, regexp);
-}
-
 /**
  * A helper function implementing the logic for both String.prototype.padStart
  * and String.prototype.padEnd as described in ES7 Draft March 29, 2016
  */
 function String_pad(maxLength, fillString, padEnd=false) {
 
     // Steps 1-2.
     RequireObjectCoercible(this);
@@ -115,253 +48,16 @@ function String_pad(maxLength, fillStrin
 function String_pad_start(maxLength, fillString=" ") {
     return callFunction(String_pad, this, maxLength, fillString, false);
 }
 
 function String_pad_end(maxLength, fillString=" ") {
     return callFunction(String_pad, this, maxLength, fillString, true);
 }
 
-function StringProtoHasNoReplace() {
-    var ObjectProto = GetBuiltinPrototype("Object");
-    var StringProto = GetBuiltinPrototype("String");
-    if (!ObjectHasPrototype(StringProto, ObjectProto))
-        return false;
-    return !(std_replace in StringProto);
-}
-
-// A thin wrapper to call SubstringKernel with int32-typed arguments.
-// Caller should check the range of |from| and |length|.
-function Substring(str, from, length) {
-    assert(typeof str === "string", "|str| should be a string");
-    assert(from | 0 === from, "coercing |from| into int32 should not change the value");
-    assert(length | 0 === length, "coercing |length| into int32 should not change the value");
-
-    return SubstringKernel(str, from | 0, length | 0);
-}
-
-// ES 2016 draft Mar 25, 2016 21.1.3.14.
-function String_replace(searchValue, replaceValue) {
-    // Step 1.
-    RequireObjectCoercible(this);
-
-    // Step 2.
-    if (!(typeof searchValue === "string" && StringProtoHasNoReplace()) &&
-        searchValue !== undefined && searchValue !== null)
-    {
-        // Step 2.a.
-        var replacer = searchValue[std_replace];
-
-        // Step 2.b.
-        if (replacer !== undefined)
-            return callContentFunction(replacer, searchValue, this, replaceValue);
-    }
-
-    // Step 3.
-    var string = ToString(this);
-
-    // Step 4.
-    var searchString = ToString(searchValue);
-
-    // FIXME: Non-standard flags argument (bug 1108382).
-    var flags = undefined;
-    if (arguments.length > 2) {
-        WarnOnceAboutFlagsArgument();
-        if (IsMatchFlagsArgumentEnabled()) {
-            flags = ToString(arguments[2]);
-            var rx = RegExpCreate(RegExpEscapeMetaChars(searchString), flags);
-
-            return callContentFunction(GetMethod(rx, std_replace), rx, string, replaceValue);
-        }
-    }
-
-    if (typeof replaceValue === "string") {
-        // Steps 6-12: Optimized for string case.
-        return StringReplaceString(string, searchString, replaceValue);
-    }
-
-    // Step 5.
-    if (!IsCallable(replaceValue)) {
-        // Steps 6-12.
-        return StringReplaceString(string, searchString, ToString(replaceValue));
-    }
-
-    // Step 7.
-    var pos = callFunction(std_String_indexOf, string, searchString);
-    if (pos === -1)
-        return string;
-
-    // Step 8.
-    var replStr = ToString(callContentFunction(replaceValue, undefined, searchString, pos, string));
-
-    // Step 10.
-    var tailPos = pos + searchString.length;
-
-    // Step 11.
-    var newString;
-    if (pos === 0)
-        newString = "";
-    else
-        newString = Substring(string, 0, pos);
-
-    newString += replStr;
-    var stringLength = string.length;
-    if (tailPos < stringLength)
-        newString += Substring(string, tailPos, stringLength - tailPos);
-
-    // Step 12.
-    return newString;
-}
-
-function String_generic_replace(thisValue, searchValue, replaceValue) {
-    if (thisValue === undefined)
-        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.replace');
-    return callFunction(String_replace, thisValue, searchValue, replaceValue);
-}
-
-function StringProtoHasNoSearch() {
-    var ObjectProto = GetBuiltinPrototype("Object");
-    var StringProto = GetBuiltinPrototype("String");
-    if (!ObjectHasPrototype(StringProto, ObjectProto))
-        return false;
-    return !(std_search in StringProto);
-}
-
-function IsStringSearchOptimizable() {
-    var RegExpProto = GetBuiltinPrototype("RegExp");
-    // If RegExpPrototypeOptimizable succeeds, `exec` and `@@search` are
-    // guaranteed to be data properties.
-    return RegExpPrototypeOptimizable(RegExpProto) &&
-           RegExpProto.exec === RegExp_prototype_Exec &&
-           RegExpProto[std_search] === RegExpSearch;
-}
-
-// ES 2016 draft Mar 25, 2016 21.1.3.15.
-function String_search(regexp) {
-    // Step 1.
-    RequireObjectCoercible(this);
-
-    // Step 2.
-    var isPatternString = (typeof regexp === "string");
-    if (!(isPatternString && StringProtoHasNoSearch()) && regexp !== undefined && regexp !== null) {
-        // Step 2.a.
-        var searcher = regexp[std_search];
-
-        // Step 2.b.
-        if (searcher !== undefined)
-            return callContentFunction(searcher, regexp, this);
-    }
-
-    // Step 3.
-    var string = ToString(this);
-
-    // FIXME: Non-standard flags argument (bug 1108382).
-    var flags = undefined;
-    if (arguments.length > 1) {
-        if (IsMatchFlagsArgumentEnabled())
-            flags = ToString(arguments[1]);
-        WarnOnceAboutFlagsArgument();
-    } else {
-        if (isPatternString && IsStringSearchOptimizable()) {
-            var flatResult = FlatStringSearch(string, regexp);
-            if (flatResult !== -2)
-                return flatResult;
-        }
-    }
-
-    // Step 4.
-    var rx = RegExpCreate(regexp, flags);
-
-    // Step 5.
-    return callContentFunction(GetMethod(rx, std_search), rx, string);
-}
-
-function String_generic_search(thisValue, regexp) {
-    if (thisValue === undefined)
-        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.search');
-    return callFunction(String_search, thisValue, regexp);
-}
-
-function StringProtoHasNoSplit() {
-    var ObjectProto = GetBuiltinPrototype("Object");
-    var StringProto = GetBuiltinPrototype("String");
-    if (!ObjectHasPrototype(StringProto, ObjectProto))
-        return false;
-    return !(std_split in StringProto);
-}
-
-// ES 2016 draft Mar 25, 2016 21.1.3.17.
-function String_split(separator, limit) {
-    // Step 1.
-    RequireObjectCoercible(this);
-
-    // Optimized path for string.split(string), especially when both strings
-    // are constants.  Following sequence of if's cannot be put together in
-    // order that IonMonkey sees the constant if present (bug 1246141).
-    if (typeof this === "string") {
-        if (StringProtoHasNoSplit()) {
-            if (typeof separator === "string") {
-                if (limit === undefined) {
-                    // inlineConstantStringSplitString needs both arguments to
-                    // be MConstant, so pass them directly.
-                    return StringSplitString(this, separator);
-                }
-            }
-        }
-    }
-
-    // Step 2.
-    if (!(typeof separator == "string" && StringProtoHasNoSplit()) &&
-        separator !== undefined && separator !== null)
-    {
-        // Step 2.a.
-        var splitter = separator[std_split];
-
-        // Step 2.b.
-        if (splitter !== undefined)
-            return callContentFunction(splitter, separator, this, limit);
-    }
-
-    // Step 3.
-    var S = ToString(this);
-
-    // Step 9 (reordered).
-    var R = ToString(separator);
-
-    // Step 6.
-    if (limit !== undefined) {
-        var lim = limit >>> 0;
-
-        // Step 10.
-        if (lim === 0)
-            return [];
-
-        // Step 11.
-        if (separator === undefined)
-            return [S];
-
-        // Steps 4, 8, 12-18.
-        return StringSplitStringLimit(S, R, lim);
-    }
-
-    // Step 11.
-    if (separator === undefined)
-        return [S];
-
-    // Optimized path.
-    // Steps 4, 8, 12-18.
-    return StringSplitString(S, R);
-}
-
-function String_generic_split(thisValue, separator, limit) {
-    if (thisValue === undefined)
-        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.split');
-    return callFunction(String_split, thisValue, separator, limit);
-}
-
 /* ES6 Draft Oct 14, 2014 21.1.3.19 */
 function String_substring(start, end) {
     // Steps 1-3.
     RequireObjectCoercible(this);
     var str = ToString(this);
 
     // Step 4.
     var len = str.length;
@@ -824,16 +520,42 @@ function String_fontsize(size) {
 
 // ES6 draft 2014-04-27 B.2.3.10
 function String_link(url) {
     RequireObjectCoercible(this);
     var S = ToString(this);
     return '<a href="' + EscapeAttributeValue(url) + '">' + S + "</a>";
 }
 
+function String_static_match(string, regexp) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.match');
+    return callFunction(std_String_match, string, regexp);
+}
+
+function String_static_replace(string, find, replacement) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.replace');
+    return callFunction(std_String_replace, string, find, replacement);
+}
+
+function String_static_search(string, regexp) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.search');
+    return callFunction(std_String_search, string, regexp);
+}
+
+function String_static_split(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.split');
+    var seperator = arguments.length > 1 ? arguments[1] : undefined;
+    var limit = arguments.length > 2 ? arguments[2] : undefined;
+    return callFunction(std_String_split, string, seperator, limit);
+}
+
 function String_static_toLowerCase(string) {
     if (arguments.length < 1)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLowerCase');
     return callFunction(std_String_toLowerCase, string);
 }
 
 function String_static_toUpperCase(string) {
     if (arguments.length < 1)
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -15,21 +15,19 @@ basic/testManyVars.js
 basic/testTypedArrayInit.js
 debug/DebuggeeWouldRun-01.js
 debug/DebuggeeWouldRun-02.js
 gc/bug-1014972.js
 gc/bug-1246593.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
-ion/bug977966.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
 parser/bug-1263881-1.js
 parser/bug-1263881-2.js
 parser/bug-1263881-3.js
 saved-stacks/bug-1006876-too-much-recursion.js
 self-test/assertDeepEq.js
-sunspider/check-string-unpack-code.js
 v8-v5/check-earley-boyer.js
 v8-v5/check-raytrace.js
 v8-v5/check-regexp.js
 v8-v5/check-splay.js
--- a/js/src/jit-test/tests/basic/bug754150.js
+++ b/js/src/jit-test/tests/basic/bug754150.js
@@ -1,18 +1,9 @@
 // |jit-test| error: TypeError;
-
-// String.prototype.replace takes too long time with gczeal(4) if
-// --no-baseline --no-ion options.
-if (typeof inJit == "function" && typeof inJit() == "string") {
-  assertEq(inJit(), "Baseline is disabled.");
-  // This test expects TypeError.
-  toPrinted(null);
-}
-
 function printStatus (msg) {}
 function toPrinted(value) {
   value = value.replace(/\\n/g, 'NL')
 }
 function reportCompare (expected, actual, description) {
     printStatus ("Expected value '" + toPrinted(expected) +  "' matched actual value '" + toPrinted(actual) + "'");
 }
 var UBound = 0;
--- a/js/src/jit-test/tests/ion/bug977966.js
+++ b/js/src/jit-test/tests/ion/bug977966.js
@@ -110,17 +110,17 @@ function split_join_multiple(i) {
 
     var s2 = "abc";
     assertEq(s2.split("").join("" + i)   , "a" + i + "b" + i + "c");
     assertEq(s2.replace("", "" + i)      , i + "abc");
     // SpiderMonkey extension
     assertEq(s2.replace("", "" + i, "g") , i + "a" + i + "b" + i + "c" + i);
 }
 
-for (var i = 0; i < 1000; ++i) {
+for (var i = 0; i < 100; ++i) {
     join_check(i);
     split(i);
     join(i);
     split_join(i);
     split_join_2(i);
     split_join_3(i);
     split_join_4(i);
     split_join_5(i);
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -904,42 +904,33 @@ function rregexp_replace(i) {
 var uceFault_regexp_y_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_replace'))
 function rregexp_y_replace(i) {
     var re = new RegExp("str\\d+" + (i % 10), "y");
     re.test("str00123456789");
     assertEq(re.lastIndex == 0, false);
 
     var res = "str00123456789".replace(re, "abc");
 
-    assertEq(re.lastIndex, 0);
-
-    assertEq(res, "str00123456789");
-
-    res = "str00123456789".replace(re, "abc");
+    // replace will not zero the lastIndex field, if sticky flag is set
     assertEq(re.lastIndex == 0, false);
 
     if (uceFault_regexp_y_replace(i) || uceFault_regexp_y_replace(i))
         assertEq(res, "abc");
     assertRecoveredOnBailout(res, false);
     return i;
 }
 
 var uceFault_regexp_y_literal_replace = eval(uneval(uceFault).replace('uceFault', 'uceFault_regexp_y_literal_replace'))
 function rregexp_y_literal_replace(i) {
     var re = /str\d+9/y;
     re.test("str00123456789");
     assertEq(re.lastIndex == 0, false);
 
     var res = "str00123456789".replace(re, "abc");
 
-    assertEq(re.lastIndex, 0);
-
-    assertEq(res, "str00123456789");
-
-    res = "str00123456789".replace(re, "abc");
     assertEq(re.lastIndex == 0, false);
 
     if (uceFault_regexp_y_literal_replace(i) || uceFault_regexp_y_literal_replace(i))
         assertEq(res, "abc");
     assertRecoveredOnBailout(res, false);
     return i;
 }
 
@@ -1058,17 +1049,17 @@ var uceFault_string_replace_y = eval(une
 function rstring_replace_y(i) {
     var re = /str\d+9/y;
 
     assertEq(re.lastIndex == 0, true);
     var res = "str00123456789".replace(re, "abc");
     if (uceFault_string_replace_y(i) || uceFault_string_replace_y(i))
         assertEq(res, "abc");
     assertRecoveredOnBailout(res, false);
-    assertEq(re.lastIndex == 0, false);
+    assertEq(re.lastIndex == 0, true);
     return i;
 }
 
 var uceFault_string_replace_g = eval(uneval(uceFault).replace('uceFault', 'uceFault_string_replace_g'))
 function rstring_replace_g(i) {
     var re = /str\d+9/g;
 
     assertEq(re.lastIndex == 0, true);
deleted file mode 100644
--- a/js/src/jit-test/tests/ion/testObjectHasPrototype.js
+++ /dev/null
@@ -1,63 +0,0 @@
-setJitCompilerOption("ion.warmup.trigger", 4);
-
-var ObjectHasPrototype = getSelfHostedValue("ObjectHasPrototype");
-
-var StringProto = String.prototype;
-var ObjectProto = Object.prototype;
-
-function testBasic() {
-  var f = function() {
-    assertEq(ObjectHasPrototype(StringProto, ObjectProto), true);
-  };
-  for (var i = 0; i < 40; i++) {
-    f();
-  }
-}
-testBasic();
-
-function testProtoChange(proto) {
-  var f = function(expected) {
-    assertEq(ObjectHasPrototype(StringProto, ObjectProto), expected);
-  };
-  var expected = true;
-  for (var i = 0; i < 120; i++) {
-    f(expected);
-    if (i == 40) {
-      Object.setPrototypeOf(StringProto, proto);
-      expected = false;
-    }
-    if (i == 80) {
-      Object.setPrototypeOf(StringProto, ObjectProto);
-      expected = true;
-    }
-  }
-}
-testProtoChange(null);
-// Different singleton
-testProtoChange(Function.prototype);
-// native non-singleton
-testProtoChange(/a/);
-// non-native non-singleton
-testProtoChange({});
-
-var Int32ArrayProto = Int32Array.prototype;
-var TypedArrayProto = Object.getPrototypeOf(Int32ArrayProto);
-function testProtoProtoChange(proto) {
-  var f = function() {
-    assertEq(ObjectHasPrototype(Int32ArrayProto, TypedArrayProto), true);
-  };
-  for (var i = 0; i < 120; i++) {
-    f();
-    if (i == 40)
-      Object.setPrototypeOf(TypedArrayProto, proto);
-    if (i == 80)
-      Object.setPrototypeOf(TypedArrayProto, Object);
-  }
-}
-testProtoProtoChange(null);
-// Different singleton
-testProtoProtoChange(Function.prototype);
-// native non-singleton
-testProtoProtoChange(/a/);
-// non-native non-singleton
-testProtoProtoChange({});
--- a/js/src/jit-test/tests/ion/testStringMatch.js
+++ b/js/src/jit-test/tests/ion/testStringMatch.js
@@ -32,16 +32,23 @@ function testMod(apply, unapply) {
       applied = true;
     }
     if (i == 80) {
       unapply();
       applied = false;
     }
   }
 }
+/*
+ * Temporary disabled, since this has been backed out.
+ * This will ride the train till it is eventually not in the tree
+ * anymore.
+ */
+
+/*
 testMod(() => {
   String.prototype[Symbol.match] = () => ["mod"];
 }, () => {
   delete String.prototype[Symbol.match];
 });
 testMod(() => {
   Object.prototype[Symbol.match] = () => ["mod"];
 }, () => {
@@ -64,8 +71,58 @@ testMod(() => {
 });
 
 var orig_match = RegExp.prototype[Symbol.match];
 testMod(() => {
   RegExp.prototype[Symbol.match] = () => ["mod"];
 }, () => {
   RegExp.prototype[Symbol.match] = orig_match;
 });
+
+var observed = false;
+function testObserved(apply, unapply) {
+  var f = function(applied) {
+    observed = false;
+    var result = "abc".match("b."); // Use meta char to avoid flat match.
+    assertEq(result.length, 1);
+    assertEq(result.index, 1);
+    assertEq(result[0], "bc");
+    assertEq(observed, applied);
+  };
+  var applied = false;
+  for (var i = 0; i < 120; i++) {
+    f(applied);
+    if (i == 40) {
+      apply();
+      applied = true;
+    }
+    if (i == 80) {
+      unapply();
+      applied = false;
+    }
+  }
+}
+
+var orig_global = Object.getOwnPropertyDescriptor(RegExp.prototype, "global");
+testObserved(() => {
+  Object.defineProperty(RegExp.prototype, "global", {
+    get: function() {
+      observed = true;
+      return false;
+    }
+  });
+}, () => {
+  Object.defineProperty(RegExp.prototype, "global", orig_global);
+});
+
+var orig_sticky = Object.getOwnPropertyDescriptor(RegExp.prototype, "sticky");
+testObserved(() => {
+  Object.defineProperty(RegExp.prototype, "sticky", {
+    get: function() {
+      observed = true;
+      return false;
+    }
+  });
+}, () => {
+  Object.defineProperty(RegExp.prototype, "sticky", orig_sticky);
+});
+
+*/
--- a/js/src/jit-test/tests/self-hosting/GetStringDataProperty.js
+++ b/js/src/jit-test/tests/self-hosting/GetStringDataProperty.js
@@ -1,11 +1,17 @@
 // Bug 1267364 - GetStringDataProperty should return undefined when the object
 // is non-native.
 
+/*
+ * Temporary disabled, since this has been backed out.
+ * This will ride the train till it is eventually not in the tree
+ * anymore.
+ */
+/*
 var GetStringDataProperty = getSelfHostedValue("GetStringDataProperty");
 
 function testProxy() {
     var obj = new Proxy({"foo": "10"}, {});
     var v = GetStringDataProperty(obj, "foo");
     assertEq(v, undefined);
 }
 
@@ -26,8 +32,9 @@ function testTypedObject() {
     var v = GetStringDataProperty(obj, "foo");
     assertEq(v, undefined);
 }
 
 testProxy();
 testMaybeUnboxed();
 if (typeof TypedObject !== "undefined")
     testTypedObject();
+*/
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -25,17 +25,16 @@
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/SharedICHelpers.h"
 #include "jit/VMFunctions.h"
 #include "js/Conversions.h"
 #include "js/GCVector.h"
 #include "vm/Opcodes.h"
-#include "vm/SelfHosting.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
@@ -5507,19 +5506,17 @@ GetTemplateObjectForNative(JSContext* cx
                 }
                 res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
                                                             TenuredObject));
                 return !!res;
             }
         }
     }
 
-    if (native == js::intrinsic_StringSplitString && args.length() == 2 && args[0].isString() &&
-        args[1].isString())
-    {
+    if (native == js::str_split && args.length() == 1 && args[0].isString()) {
         ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
         if (!group)
             return false;
         if (group->maybePreliminaryObjects()) {
             *skipAttach = true;
             return true;
         }
 
@@ -5562,29 +5559,29 @@ GetTemplateObjectForClassHook(JSContext*
         templateObject.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr));
         return !!templateObject;
     }
 
     return true;
 }
 
 static bool
-IsOptimizableCallStringSplit(Value callee, int argc, Value* args)
-{
-    if (argc != 2 || !args[0].isString() || !args[1].isString())
+IsOptimizableCallStringSplit(Value callee, Value thisv, int argc, Value* args)
+{
+    if (argc != 1 || !thisv.isString() || !args[0].isString())
         return false;
 
-    if (!args[0].toString()->isAtom() || !args[1].toString()->isAtom())
+    if (!thisv.toString()->isAtom() || !args[0].toString()->isAtom())
         return false;
 
     if (!callee.isObject() || !callee.toObject().is<JSFunction>())
         return false;
 
     JSFunction& calleeFun = callee.toObject().as<JSFunction>();
-    if (!calleeFun.isNative() || calleeFun.native() != js::intrinsic_StringSplitString)
+    if (!calleeFun.isNative() || calleeFun.native() != js::str_split)
         return false;
 
     return true;
 }
 
 static bool
 TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
                   JSOp op, uint32_t argc, Value* vp, bool constructing, bool isSpread,
@@ -5601,17 +5598,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
         return true;
     }
 
     RootedValue callee(cx, vp[0]);
     RootedValue thisv(cx, vp[1]);
 
     // Don't attach an optimized call stub if we could potentially attach an
     // optimized StringSplit stub.
-    if (stub->numOptimizedStubs() == 0 && IsOptimizableCallStringSplit(callee, argc, vp + 2))
+    if (stub->numOptimizedStubs() == 0 && IsOptimizableCallStringSplit(callee, thisv, argc, vp + 2))
         return true;
 
     MOZ_ASSERT_IF(stub->hasStub(ICStub::Call_StringSplit), stub->numOptimizedStubs() == 1);
 
     stub->unlinkStubsWithKind(cx, ICStub::Call_StringSplit);
 
     if (!callee.isObject())
         return true;
@@ -5849,30 +5846,31 @@ CopyArray(JSContext* cx, HandleObject ob
 static bool
 TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
                      uint32_t argc, HandleValue callee, Value* vp, jsbytecode* pc,
                      HandleValue res, bool* attached)
 {
     if (stub->numOptimizedStubs() != 0)
         return true;
 
+    RootedValue thisv(cx, vp[1]);
     Value* args = vp + 2;
 
     // String.prototype.split will not yield a constructable.
     if (JSOp(*pc) == JSOP_NEW)
         return true;
 
-    if (!IsOptimizableCallStringSplit(callee, argc, args))
+    if (!IsOptimizableCallStringSplit(callee, thisv, argc, args))
         return true;
 
     MOZ_ASSERT(callee.isObject());
     MOZ_ASSERT(callee.toObject().is<JSFunction>());
 
-    RootedString str(cx, args[0].toString());
-    RootedString sep(cx, args[1].toString());
+    RootedString thisString(cx, thisv.toString());
+    RootedString argString(cx, args[0].toString());
     RootedObject obj(cx, &res.toObject());
     RootedValue arr(cx);
 
     // Copy the array before storing in stub.
     if (!CopyArray(cx, obj, &arr))
         return false;
 
     // Atomize all elements of the array.
@@ -5885,17 +5883,17 @@ TryAttachStringSplit(JSContext* cx, ICCa
 
         if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) {
             // The value could not be stored to an unboxed dense element.
             return true;
         }
     }
 
     ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                          script->pcToOffset(pc), str, sep,
+                                          script->pcToOffset(pc), thisString, argString,
                                           arr);
     ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
     stub->addNewStub(newStub);
     *attached = true;
     return true;
@@ -6779,82 +6777,77 @@ ICCallScriptedCompiler::generateStubCode
 typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue);
 static const VMFunction CopyArrayInfo = FunctionInfo<CopyArrayFn>(CopyArray);
 
 bool
 ICCall_StringSplit::Compiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
 
-    // Stack Layout: [ ..., CalleeVal, ThisVal, strVal, sepVal, +ICStackValueOffset+ ]
-    static const size_t SEP_DEPTH = 0;
-    static const size_t STR_DEPTH = sizeof(Value);
-    static const size_t CALLEE_DEPTH = 3 * sizeof(Value);
-
+    // Stack Layout: [ ..., CalleeVal, ThisVal, Arg0Val, +ICStackValueOffset+ ]
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
     Label failureRestoreArgc;
 #ifdef DEBUG
-    Label twoArg;
+    Label oneArg;
     Register argcReg = R0.scratchReg();
-    masm.branch32(Assembler::Equal, argcReg, Imm32(2), &twoArg);
-    masm.assumeUnreachable("Expected argc == 2");
-    masm.bind(&twoArg);
+    masm.branch32(Assembler::Equal, argcReg, Imm32(1), &oneArg);
+    masm.assumeUnreachable("Expected argc == 1");
+    masm.bind(&oneArg);
 #endif
     Register scratchReg = regs.takeAny();
 
-    // Guard that callee is native function js::intrinsic_StringSplitString.
+    // Guard that callee is native function js::str_split.
     {
-        Address calleeAddr(masm.getStackPointer(), ICStackValueOffset + CALLEE_DEPTH);
+        Address calleeAddr(masm.getStackPointer(), ICStackValueOffset + (2 * sizeof(Value)));
         ValueOperand calleeVal = regs.takeAnyValue();
 
         // Ensure that callee is an object.
         masm.loadValue(calleeAddr, calleeVal);
         masm.branchTestObject(Assembler::NotEqual, calleeVal, &failureRestoreArgc);
 
         // Ensure that callee is a function.
         Register calleeObj = masm.extractObject(calleeVal, ExtractTemp0);
         masm.branchTestObjClass(Assembler::NotEqual, calleeObj, scratchReg,
                                 &JSFunction::class_, &failureRestoreArgc);
 
-        // Ensure that callee's function impl is the native intrinsic_StringSplitString.
+        // Ensure that callee's function impl is the native str_split.
         masm.loadPtr(Address(calleeObj, JSFunction::offsetOfNativeOrScript()), scratchReg);
-        masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(js::intrinsic_StringSplitString),
-                       &failureRestoreArgc);
+        masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(js::str_split), &failureRestoreArgc);
 
         regs.add(calleeVal);
     }
 
-    // Guard sep.
+    // Guard argument.
     {
-        // Ensure that sep is a string.
-        Address sepAddr(masm.getStackPointer(), ICStackValueOffset + SEP_DEPTH);
-        ValueOperand sepVal = regs.takeAnyValue();
-
-        masm.loadValue(sepAddr, sepVal);
-        masm.branchTestString(Assembler::NotEqual, sepVal, &failureRestoreArgc);
-
-        Register sep = masm.extractString(sepVal, ExtractTemp0);
-        masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedSep()),
-                       sep, &failureRestoreArgc);
-        regs.add(sepVal);
-    }
-
-    // Guard str.
+        // Ensure that arg is a string.
+        Address argAddr(masm.getStackPointer(), ICStackValueOffset);
+        ValueOperand argVal = regs.takeAnyValue();
+
+        masm.loadValue(argAddr, argVal);
+        masm.branchTestString(Assembler::NotEqual, argVal, &failureRestoreArgc);
+
+        Register argString = masm.extractString(argVal, ExtractTemp0);
+        masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedArg()),
+                       argString, &failureRestoreArgc);
+        regs.add(argVal);
+    }
+
+    // Guard this-value.
     {
-        // Ensure that str is a string.
-        Address strAddr(masm.getStackPointer(), ICStackValueOffset + STR_DEPTH);
-        ValueOperand strVal = regs.takeAnyValue();
-
-        masm.loadValue(strAddr, strVal);
-        masm.branchTestString(Assembler::NotEqual, strVal, &failureRestoreArgc);
-
-        Register str = masm.extractString(strVal, ExtractTemp0);
-        masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedStr()),
-                       str, &failureRestoreArgc);
-        regs.add(strVal);
+        // Ensure that thisv is a string.
+        Address thisvAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value));
+        ValueOperand thisvVal = regs.takeAnyValue();
+
+        masm.loadValue(thisvAddr, thisvVal);
+        masm.branchTestString(Assembler::NotEqual, thisvVal, &failureRestoreArgc);
+
+        Register thisvString = masm.extractString(thisvVal, ExtractTemp0);
+        masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedThis()),
+                       thisvString, &failureRestoreArgc);
+        regs.add(thisvVal);
     }
 
     // Main stub body.
     {
         Register paramReg = regs.takeAny();
 
         // Push arguments.
         enterStubFrame(masm, scratchReg);
@@ -6867,17 +6860,17 @@ ICCall_StringSplit::Compiler::generateSt
         regs.add(paramReg);
     }
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
     // Guard failure path.
     masm.bind(&failureRestoreArgc);
-    masm.move32(Imm32(2), R0.scratchReg());
+    masm.move32(Imm32(1), R0.scratchReg());
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
 ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -2862,81 +2862,81 @@ class ICCall_ScriptedFunCall : public IC
 };
 
 class ICCall_StringSplit : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
   protected:
     uint32_t pcOffset_;
-    HeapPtrString expectedStr_;
-    HeapPtrString expectedSep_;
+    HeapPtrString expectedThis_;
+    HeapPtrString expectedArg_;
     HeapPtrObject templateObject_;
 
-    ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* str,
-                       JSString* sep, JSObject* templateObject)
+    ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* thisString,
+                       JSString* argString, JSObject* templateObject)
       : ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
-        pcOffset_(pcOffset), expectedStr_(str), expectedSep_(sep),
+        pcOffset_(pcOffset), expectedThis_(thisString), expectedArg_(argString),
         templateObject_(templateObject)
     { }
 
   public:
-    static size_t offsetOfExpectedStr() {
-        return offsetof(ICCall_StringSplit, expectedStr_);
+    static size_t offsetOfExpectedThis() {
+        return offsetof(ICCall_StringSplit, expectedThis_);
     }
 
-    static size_t offsetOfExpectedSep() {
-        return offsetof(ICCall_StringSplit, expectedSep_);
+    static size_t offsetOfExpectedArg() {
+        return offsetof(ICCall_StringSplit, expectedArg_);
     }
 
     static size_t offsetOfTemplateObject() {
         return offsetof(ICCall_StringSplit, templateObject_);
     }
 
-    HeapPtrString& expectedStr() {
-        return expectedStr_;
+    HeapPtrString& expectedThis() {
+        return expectedThis_;
     }
 
-    HeapPtrString& expectedSep() {
-        return expectedSep_;
+    HeapPtrString& expectedArg() {
+        return expectedArg_;
     }
 
     HeapPtrObject& templateObject() {
         return templateObject_;
     }
 
     class Compiler : public ICCallStubCompiler {
       protected:
         ICStub* firstMonitorStub_;
         uint32_t pcOffset_;
-        RootedString expectedStr_;
-        RootedString expectedSep_;
+        RootedString expectedThis_;
+        RootedString expectedArg_;
         RootedObject templateObject_;
 
         bool generateStubCode(MacroAssembler& masm);
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(engine_) |
                   (static_cast<int32_t>(kind) << 1);
         }
 
       public:
-        Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString str,
-                 HandleString sep, HandleValue templateObject)
+        Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString thisString,
+                 HandleString argString, HandleValue templateObject)
           : ICCallStubCompiler(cx, ICStub::Call_StringSplit),
             firstMonitorStub_(firstMonitorStub),
             pcOffset_(pcOffset),
-            expectedStr_(cx, str),
-            expectedSep_(cx, sep),
+            expectedThis_(cx, thisString),
+            expectedArg_(cx, argString),
             templateObject_(cx, &templateObject.toObject())
         { }
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCall_StringSplit>(space, getStubCode(), firstMonitorStub_, pcOffset_,
-                                               expectedStr_, expectedSep_, templateObject_);
+                                               expectedThis_, expectedArg_, templateObject_);
         }
    };
 };
 
 class ICCall_IsSuspendedStarGenerator : public ICStub
 {
     friend class ICStubSpace;
 
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -568,34 +568,34 @@ BaselineInspector::getTemplateObjectForN
         if (stub->isCall_Native() && stub->toCall_Native()->callee()->native() == native)
             return stub->toCall_Native()->templateObject();
     }
 
     return nullptr;
 }
 
 bool
-BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut,
+BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
                                                 JSObject** objOut)
 {
     if (!hasBaselineScript())
         return false;
 
     const ICEntry& entry = icEntryFromPC(pc);
 
     // If StringSplit stub is attached, must have only one stub attached.
     if (entry.fallbackStub()->numOptimizedStubs() != 1)
         return false;
 
     ICStub* stub = entry.firstStub();
     if (stub->kind() != ICStub::Call_StringSplit)
         return false;
 
-    *strOut = stub->toCall_StringSplit()->expectedStr();
-    *sepOut = stub->toCall_StringSplit()->expectedSep();
+    *stringOut = stub->toCall_StringSplit()->expectedThis();
+    *stringArg = stub->toCall_StringSplit()->expectedArg();
     *objOut = stub->toCall_StringSplit()->templateObject();
     return true;
 }
 
 JSObject*
 BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp)
 {
     if (!hasBaselineScript())
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -107,17 +107,17 @@ class BaselineInspector
     MIRType expectedPropertyAccessInputType(jsbytecode* pc);
 
     bool hasSeenNonNativeGetElement(jsbytecode* pc);
     bool hasSeenNegativeIndexGetElement(jsbytecode* pc);
     bool hasSeenAccessedGetter(jsbytecode* pc);
     bool hasSeenDoubleResult(jsbytecode* pc);
     bool hasSeenNonStringIterMore(jsbytecode* pc);
 
-    bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut,
+    bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** stringOut, JSString** stringArg,
                                       JSObject** objOut);
     JSObject* getTemplateObject(jsbytecode* pc);
     JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
     JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
 
     // Sometimes the group a template object will have is known, even if the
     // object itself isn't.
     ObjectGroup* getTemplateObjectGroup(jsbytecode* pc);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -15,17 +15,16 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsprf.h"
-#include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "builtin/TypedObject.h"
 #include "gc/Nursery.h"
 #include "irregexp/NativeRegExpMacroAssembler.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/IonBuilder.h"
@@ -35,17 +34,16 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "vm/MatchPairs.h"
-#include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
 #include "vm/Unicode.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
@@ -1036,17 +1034,17 @@ RegExpPairCountAddress(MacroAssembler& m
 }
 
 // Prepare an InputOutputData and optional MatchPairs which space has been
 // allocated for on the stack, and try to execute a RegExp on a string input.
 // If the RegExp was successfully executed and matched the input, fallthrough,
 // otherwise jump to notFound or failure.
 static bool
 PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Register input,
-                        Register lastIndex,
+                        Register lastIndex, Register sticky,
                         Register temp1, Register temp2, Register temp3,
                         size_t inputOutputDataStartOffset,
                         RegExpShared::CompilationMode mode,
                         Label* notFound, Label* failure)
 {
     size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
 
@@ -1147,28 +1145,49 @@ PrepareAndExecuteRegExp(JSContext* cx, M
     // Load the code pointer for the type of input string we have, and compute
     // the input start/end pointers in the InputOutputData.
     Register codePointer = temp1;
     {
         masm.loadStringChars(input, temp2);
         masm.storePtr(temp2, inputStartAddress);
         masm.loadStringLength(input, temp3);
 
-        Label isLatin1, done;
-        masm.branchLatin1String(input, &isLatin1);
+        Label stickyCode, done;
+        masm.branchTest32(Assembler::NonZero, sticky, sticky, &stickyCode);
         {
-            masm.lshiftPtr(Imm32(1), temp3);
-            masm.loadPtr(Address(temp1, RegExpShared::offsetOfTwoByteJitCode(mode)),
-                         codePointer);
+            Label isLatin1;
+            masm.branchLatin1String(input, &isLatin1);
+            {
+                masm.lshiftPtr(Imm32(1), temp3);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyTwoByteJitCode(mode)),
+                             codePointer);
+            }
+            masm.jump(&done);
+            {
+                masm.bind(&isLatin1);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyLatin1JitCode(mode)),
+                             codePointer);
+            }
         }
         masm.jump(&done);
         {
-            masm.bind(&isLatin1);
-            masm.loadPtr(Address(temp1, RegExpShared::offsetOfLatin1JitCode(mode)),
-                         codePointer);
+            masm.bind(&stickyCode);
+            Label isLatin1;
+            masm.branchLatin1String(input, &isLatin1);
+            {
+                masm.lshiftPtr(Imm32(1), temp3);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyTwoByteJitCode(mode)),
+                             codePointer);
+            }
+            masm.jump(&done);
+            {
+                masm.bind(&isLatin1);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyLatin1JitCode(mode)),
+                             codePointer);
+            }
         }
         masm.bind(&done);
 
         masm.addPtr(temp3, temp2);
         masm.storePtr(temp2, inputEndAddress);
     }
 
     // Check the RegExpShared has been compiled for this type of input.
@@ -1184,16 +1203,18 @@ PrepareAndExecuteRegExp(JSContext* cx, M
         masm.computeEffectiveAddress(endIndexAddress, temp2);
         masm.storePtr(temp2, endIndexAddress);
     }
     masm.storePtr(lastIndex, startIndexAddress);
     masm.store32(Imm32(0), matchResultAddress);
 
     // Save any volatile inputs.
     LiveGeneralRegisterSet volatileRegs;
+    if (sticky.volatile_())
+        volatileRegs.add(sticky);
     if (lastIndex.volatile_())
         volatileRegs.add(lastIndex);
     if (input.volatile_())
         volatileRegs.add(input);
     if (regexp.volatile_())
         volatileRegs.add(regexp);
 
     // Execute the RegExp.
@@ -1212,23 +1233,25 @@ PrepareAndExecuteRegExp(JSContext* cx, M
 
     // Lazily update the RegExpStatics.
     masm.movePtr(ImmPtr(res), temp1);
 
     Address pendingInputAddress(temp1, RegExpStatics::offsetOfPendingInput());
     Address matchesInputAddress(temp1, RegExpStatics::offsetOfMatchesInput());
     Address lazySourceAddress(temp1, RegExpStatics::offsetOfLazySource());
     Address lazyIndexAddress(temp1, RegExpStatics::offsetOfLazyIndex());
+    Address lazyStickyAddress(temp1, RegExpStatics::offsetOfLazySticky());
 
     masm.patchableCallPreBarrier(pendingInputAddress, MIRType_String);
     masm.patchableCallPreBarrier(matchesInputAddress, MIRType_String);
     masm.patchableCallPreBarrier(lazySourceAddress, MIRType_String);
 
     masm.storePtr(input, pendingInputAddress);
     masm.storePtr(input, matchesInputAddress);
+    masm.storePtr(sticky, lazyStickyAddress);
     masm.storePtr(lastIndex, Address(temp1, RegExpStatics::offsetOfLazyIndex()));
     masm.store32(Imm32(1), Address(temp1, RegExpStatics::offsetOfPendingLazyEvaluation()));
 
     masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp2);
     masm.loadPtr(Address(temp2, RegExpShared::offsetOfSource()), temp3);
     masm.storePtr(temp3, lazySourceAddress);
     masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
     masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
@@ -1439,18 +1462,17 @@ static void*
 CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots)
 {
     return js::Allocate<JSObject, NoGC>(cx, kind, nDynamicSlots, gc::DefaultHeap,
                                         &ArrayObject::class_);
 }
 
 static void
 CreateMatchResultFallback(MacroAssembler& masm, LiveRegisterSet regsToSave,
-                          Register object, Register temp2, Register temp5,
-                          ArrayObject* templateObj, Label* fail)
+                          Register object, Register temp2, Register temp5, ArrayObject* templateObj, Label* fail)
 {
     MOZ_ASSERT(templateObj->group()->clasp() == &ArrayObject::class_);
 
     regsToSave.take(object);
     regsToSave.take(temp2);
     regsToSave.take(temp5);
     masm.PushRegsInMask(regsToSave);
 
@@ -1473,43 +1495,45 @@ CreateMatchResultFallback(MacroAssembler
 }
 
 JitCode*
 JitCompartment::generateRegExpMatcherStub(JSContext* cx)
 {
     Register regexp = RegExpMatcherRegExpReg;
     Register input = RegExpMatcherStringReg;
     Register lastIndex = RegExpMatcherLastIndexReg;
+    Register sticky = RegExpMatcherStickyReg;
     ValueOperand result = JSReturnOperand;
 
     // We are free to clobber all registers, as LRegExpMatcher is a call instruction.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
     regs.take(regexp);
     regs.take(lastIndex);
+    regs.take(sticky);
 
     // temp5 is used in single byte instructions when creating dependent
     // strings, and has restrictions on which register it can be on some
     // platforms.
     Register temp5;
     {
         AllocatableGeneralRegisterSet oregs = regs;
         do {
             temp5 = oregs.takeAny();
         } while (!MacroAssembler::canUseInSingleByteInstruction(temp5));
         regs.take(temp5);
     }
 
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
-    Register temp3 = regs.takeAny();
-
+    Register maybeTemp3 = InvalidReg;
     Register maybeTemp4 = InvalidReg;
     if (!regs.empty()) {
         // There are not enough registers on x86.
+        maybeTemp3 = regs.takeAny();
         maybeTemp4 = regs.takeAny();
     }
 
     ArrayObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return nullptr;
 
     // The template object should have enough space for the maximum number of
@@ -1518,17 +1542,17 @@ JitCompartment::generateRegExpMatcherStu
                gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()));
 
     MacroAssembler masm(cx);
 
     // The InputOutputData is placed above the return address on the stack.
     size_t inputOutputDataStartOffset = sizeof(void*);
 
     Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
+    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
                                  temp1, temp2, temp5, inputOutputDataStartOffset,
                                  RegExpShared::Normal, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     // Construct the result.
     Register object = temp1;
@@ -1582,23 +1606,25 @@ JitCompartment::generateRegExpMatcherStu
     // Loop to construct the match strings. There are two different loops,
     // depending on whether the input is latin1.
     CreateDependentString depStr[2];
     {
         Label isLatin1, done;
         masm.branchLatin1String(input, &isLatin1);
 
         Label* failure = &oolEntry;
-        Register temp4 = (maybeTemp4 == InvalidReg) ? lastIndex : maybeTemp4;
+        Register temp3 = (maybeTemp3 == InvalidReg) ? sticky : maybeTemp3;
+        Register temp4 = (maybeTemp3 == InvalidReg) ? lastIndex : maybeTemp4;
 
         Label failureRestore;
-        if (maybeTemp4 == InvalidReg) {
+        if (maybeTemp3 == InvalidReg) {
             failure = &failureRestore;
 
-            // Save lastIndex value to temporary space.
+            // Save sticky and lastIndex values to temporary space.
+            masm.store32(sticky, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
             masm.store32(lastIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
         }
 
         for (int isLatin = 0; isLatin <= 1; isLatin++) {
             if (isLatin)
                 masm.bind(&isLatin1);
 
             Label matchLoop;
@@ -1618,24 +1644,26 @@ JitCompartment::generateRegExpMatcherStu
             masm.storeValue(UndefinedValue(), stringAddress);
             masm.bind(&storeDone);
 
             masm.add32(Imm32(1), matchIndex);
             masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, &done);
             masm.jump(&matchLoop);
         }
 
-        if (maybeTemp4 == InvalidReg) {
-            // Restore lastIndex value from temporary space, both for success
-            // and failure cases.
-
+        if (maybeTemp3 == InvalidReg) {
+            // Restore sticky and lastIndex values from temporary space, both
+            // for success and failure cases.
+
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
             masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
             masm.jump(&done);
 
             masm.bind(&failureRestore);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
             masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
 
             // Restore the match object for failure case.
             masm.store32(Imm32(templateObject->getDenseInitializedLength()),
                          Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
             masm.store32(Imm32(templateObject->length()),
                          Address(object, elementsOffset + ObjectElements::offsetOfLength()));
             masm.jump(&oolEntry);
@@ -1649,37 +1677,40 @@ JitCompartment::generateRegExpMatcherStu
     masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
 
     masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
 
     MOZ_ASSERT(templateObject->numFixedSlots() == 0);
     MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
     MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
 
-    masm.load32(pairsVectorAddress, temp3);
-    masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
+    // sticky is now free, because no more ool entry happens.
+    masm.load32(pairsVectorAddress, sticky);
+    masm.storeValue(JSVAL_TYPE_INT32, sticky, Address(temp2, 0));
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
 
     // All done!
     masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
     masm.ret();
 
     masm.bind(&notFound);
     masm.moveValue(NullValue(), result);
     masm.ret();
 
     // Fallback paths for CreateDependentString and createGCObject.
     // Need to save all registers in use when they were called.
     LiveRegisterSet regsToSave(RegisterSet::Volatile());
     regsToSave.addUnchecked(regexp);
     regsToSave.addUnchecked(input);
     regsToSave.addUnchecked(lastIndex);
+    regsToSave.addUnchecked(sticky);
     regsToSave.addUnchecked(temp1);
     regsToSave.addUnchecked(temp2);
-    regsToSave.addUnchecked(temp3);
+    if (maybeTemp3 != InvalidReg)
+        regsToSave.addUnchecked(maybeTemp3);
     if (maybeTemp4 != InvalidReg)
         regsToSave.addUnchecked(maybeTemp4);
     regsToSave.addUnchecked(temp5);
 
     for (int isLatin = 0; isLatin <= 1; isLatin++)
         depStr[isLatin].generateFallback(masm, regsToSave);
 
     masm.bind(&matchResultFallback);
@@ -1721,38 +1752,41 @@ class OutOfLineRegExpMatcher : public Ou
     }
 
     LRegExpMatcher* lir() const {
         return lir_;
     }
 };
 
 typedef bool (*RegExpMatcherRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
-                                   int32_t lastIndex,
+                                   int32_t lastIndex, bool sticky,
                                    MatchPairs* pairs, MutableHandleValue output);
 static const VMFunction RegExpMatcherRawInfo = FunctionInfo<RegExpMatcherRawFn>(RegExpMatcherRaw);
 
 void
 CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool)
 {
     LRegExpMatcher* lir = ool->lir();
+    Register sticky = ToRegister(lir->sticky());
     Register lastIndex = ToRegister(lir->lastIndex());
     Register input = ToRegister(lir->string());
     Register regexp = ToRegister(lir->regexp());
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(sticky);
     regs.take(lastIndex);
     regs.take(input);
     regs.take(regexp);
     Register temp = regs.takeAny();
 
     masm.computeEffectiveAddress(Address(masm.getStackPointer(),
         sizeof(irregexp::InputOutputData)), temp);
 
     pushArg(temp);
+    pushArg(sticky);
     pushArg(lastIndex);
     pushArg(input);
     pushArg(regexp);
 
     // We are not using oolCallVM because we are in a Call, and that live
     // registers are already saved by the the register allocator.
     callVM(RegExpMatcherRawInfo, lir);
 
@@ -1760,222 +1794,81 @@ CodeGenerator::visitOutOfLineRegExpMatch
 }
 
 void
 CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir)
 {
     MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg);
     MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg);
     MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg);
+    MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpMatcherStickyReg);
     MOZ_ASSERT(GetValueOutput(lir) == JSReturnOperand);
 
 #if defined(JS_NUNBOX32)
     MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Type);
     MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Data);
     MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Type);
     MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Data);
     MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Type);
     MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Data);
 #elif defined(JS_PUNBOX64)
     MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg);
     MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg);
     MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg);
 #endif
 
     masm.reserveStack(RegExpReservedStack);
 
     OutOfLineRegExpMatcher* ool = new(alloc()) OutOfLineRegExpMatcher(lir);
     addOutOfLineCode(ool, lir->mir());
 
     JitCode* regExpMatcherStub = gen->compartment->jitCompartment()->regExpMatcherStubNoBarrier();
     masm.call(regExpMatcherStub);
     masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
     masm.bind(ool->rejoin());
 
     masm.freeStack(RegExpReservedStack);
 }
 
-static const int32_t RegExpSearcherResultNotFound = -1;
-static const int32_t RegExpSearcherResultFailed = -2;
-
-JitCode*
-JitCompartment::generateRegExpSearcherStub(JSContext* cx)
-{
-    Register regexp = RegExpTesterRegExpReg;
-    Register input = RegExpTesterStringReg;
-    Register lastIndex = RegExpTesterLastIndexReg;
-    Register result = ReturnReg;
-
-    // We are free to clobber all registers, as LRegExpSearcher is a call instruction.
-    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(input);
-    regs.take(regexp);
-    regs.take(lastIndex);
-
-    Register temp1 = regs.takeAny();
-    Register temp2 = regs.takeAny();
-    Register temp3 = regs.takeAny();
-
-    MacroAssembler masm(cx);
-
-    // The InputOutputData is placed above the return address on the stack.
-    size_t inputOutputDataStartOffset = sizeof(void*);
-
-    Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
-                                 temp1, temp2, temp3, inputOutputDataStartOffset,
-                                 RegExpShared::Normal, &notFound, &oolEntry))
-    {
-        return nullptr;
-    }
-
-    size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
-    Address stringIndexAddress(masm.getStackPointer(),
-                               pairsVectorStartOffset + offsetof(MatchPair, start));
-    Address stringLimitAddress(masm.getStackPointer(),
-                               pairsVectorStartOffset + offsetof(MatchPair, limit));
-
-    masm.load32(stringIndexAddress, result);
-    masm.load32(stringLimitAddress, input);
-    masm.lshiftPtr(Imm32(15), input);
-    masm.or32(input, result);
-    masm.ret();
-
-    masm.bind(&notFound);
-    masm.move32(Imm32(RegExpSearcherResultNotFound), result);
-    masm.ret();
-
-    masm.bind(&oolEntry);
-    masm.move32(Imm32(RegExpSearcherResultFailed), result);
-    masm.ret();
-
-    Linker linker(masm);
-    AutoFlushICache afc("RegExpSearcherStub");
-    JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
-    if (!code)
-        return nullptr;
-
-#ifdef JS_ION_PERF
-    writePerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
-#endif
-
-    if (cx->zone()->needsIncrementalBarrier())
-        code->togglePreBarriers(true, DontReprotect);
-
-    return code;
-}
-
-class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpSearcher* lir_;
-
-  public:
-    explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir)
-      : lir_(lir)
-    { }
-
-    void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpSearcher(this);
-    }
-
-    LRegExpSearcher* lir() const {
-        return lir_;
-    }
-};
-
-typedef bool (*RegExpSearcherRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
-                                    int32_t lastIndex,
-                                    MatchPairs* pairs, int32_t* result);
-static const VMFunction RegExpSearcherRawInfo = FunctionInfo<RegExpSearcherRawFn>(RegExpSearcherRaw);
-
-void
-CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool)
-{
-    LRegExpSearcher* lir = ool->lir();
-    Register lastIndex = ToRegister(lir->lastIndex());
-    Register input = ToRegister(lir->string());
-    Register regexp = ToRegister(lir->regexp());
-
-    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(lastIndex);
-    regs.take(input);
-    regs.take(regexp);
-    Register temp = regs.takeAny();
-
-    masm.computeEffectiveAddress(Address(masm.getStackPointer(),
-        sizeof(irregexp::InputOutputData)), temp);
-
-    pushArg(temp);
-    pushArg(lastIndex);
-    pushArg(input);
-    pushArg(regexp);
-
-    // We are not using oolCallVM because we are in a Call, and that live
-    // registers are already saved by the the register allocator.
-    callVM(RegExpSearcherRawInfo, lir);
-
-    masm.jump(ool->rejoin());
-}
-
-void
-CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir)
-{
-    MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpTesterRegExpReg);
-    MOZ_ASSERT(ToRegister(lir->string()) == RegExpTesterStringReg);
-    MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpTesterLastIndexReg);
-    MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
-
-    MOZ_ASSERT(RegExpTesterRegExpReg != ReturnReg);
-    MOZ_ASSERT(RegExpTesterStringReg != ReturnReg);
-    MOZ_ASSERT(RegExpTesterLastIndexReg != ReturnReg);
-
-    masm.reserveStack(RegExpReservedStack);
-
-    OutOfLineRegExpSearcher* ool = new(alloc()) OutOfLineRegExpSearcher(lir);
-    addOutOfLineCode(ool, lir->mir());
-
-    JitCode* regExpSearcherStub = gen->compartment->jitCompartment()->regExpSearcherStubNoBarrier();
-    masm.call(regExpSearcherStub);
-    masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed), ool->entry());
-    masm.bind(ool->rejoin());
-
-    masm.freeStack(RegExpReservedStack);
-}
-
 static const int32_t RegExpTesterResultNotFound = -1;
 static const int32_t RegExpTesterResultFailed = -2;
 
 JitCode*
 JitCompartment::generateRegExpTesterStub(JSContext* cx)
 {
     Register regexp = RegExpTesterRegExpReg;
     Register input = RegExpTesterStringReg;
     Register lastIndex = RegExpTesterLastIndexReg;
+    Register sticky = RegExpTesterStickyReg;
     Register result = ReturnReg;
 
     MacroAssembler masm(cx);
 
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
 
     // We are free to clobber all registers, as LRegExpTester is a call instruction.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
     regs.take(regexp);
     regs.take(lastIndex);
+    regs.take(sticky);
 
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
     Register temp3 = regs.takeAny();
 
     masm.reserveStack(sizeof(irregexp::InputOutputData));
 
     Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex,
+    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
                                  temp1, temp2, temp3, 0,
                                  RegExpShared::MatchOnly, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     Label done;
 
@@ -2024,27 +1917,29 @@ class OutOfLineRegExpTester : public Out
     }
 
     LRegExpTester* lir() const {
         return lir_;
     }
 };
 
 typedef bool (*RegExpTesterRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
-                                  int32_t lastIndex, int32_t* result);
+                                  int32_t lastIndex, bool sticky, int32_t* result);
 static const VMFunction RegExpTesterRawInfo = FunctionInfo<RegExpTesterRawFn>(RegExpTesterRaw);
 
 void
 CodeGenerator::visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool)
 {
     LRegExpTester* lir = ool->lir();
+    Register sticky = ToRegister(lir->sticky());
     Register lastIndex = ToRegister(lir->lastIndex());
     Register input = ToRegister(lir->string());
     Register regexp = ToRegister(lir->regexp());
 
+    pushArg(sticky);
     pushArg(lastIndex);
     pushArg(input);
     pushArg(regexp);
 
     // We are not using oolCallVM because we are in a Call, and that live
     // registers are already saved by the the register allocator.
     callVM(RegExpTesterRawInfo, lir);
 
@@ -2052,170 +1947,53 @@ CodeGenerator::visitOutOfLineRegExpTeste
 }
 
 void
 CodeGenerator::visitRegExpTester(LRegExpTester* lir)
 {
     MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpTesterRegExpReg);
     MOZ_ASSERT(ToRegister(lir->string()) == RegExpTesterStringReg);
     MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpTesterLastIndexReg);
+    MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpTesterStickyReg);
     MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
 
     MOZ_ASSERT(RegExpTesterRegExpReg != ReturnReg);
     MOZ_ASSERT(RegExpTesterStringReg != ReturnReg);
     MOZ_ASSERT(RegExpTesterLastIndexReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterStickyReg != ReturnReg);
 
     OutOfLineRegExpTester* ool = new(alloc()) OutOfLineRegExpTester(lir);
     addOutOfLineCode(ool, lir->mir());
 
     JitCode* regExpTesterStub = gen->compartment->jitCompartment()->regExpTesterStubNoBarrier();
     masm.call(regExpTesterStub);
 
     masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTesterResultFailed), ool->entry());
     masm.bind(ool->rejoin());
 }
 
-class OutOfLineRegExpPrototypeOptimizable : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpPrototypeOptimizable* ins_;
-
-  public:
-    explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
-      : ins_(ins)
-    { }
-
-    void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpPrototypeOptimizable(this);
-    }
-    LRegExpPrototypeOptimizable* ins() const {
-        return ins_;
-    }
-};
-
-void
-CodeGenerator::visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
-{
-    Register object = ToRegister(ins->object());
-    Register output = ToRegister(ins->output());
-    Register temp = ToRegister(ins->temp());
-
-    OutOfLineRegExpPrototypeOptimizable* ool = new(alloc()) OutOfLineRegExpPrototypeOptimizable(ins);
-    addOutOfLineCode(ool, ins->mir());
-
-    masm.loadJSContext(temp);
-    masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
-    size_t offset = JSCompartment::offsetOfRegExps() +
-                    RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape();
-    masm.loadPtr(Address(temp, offset), temp);
-
-    masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
-    masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
-    masm.move32(Imm32(0x1), output);
-
-    masm.bind(ool->rejoin());
-}
-
-void
-CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototypeOptimizable* ool)
-{
-    LRegExpPrototypeOptimizable* ins = ool->ins();
-    Register object = ToRegister(ins->object());
-    Register output = ToRegister(ins->output());
-    Register temp = ToRegister(ins->temp());
-
-    saveVolatile(output);
-
-    masm.reserveStack(sizeof(void*));
-    masm.moveStackPtrTo(temp);
-
-    masm.setupUnalignedABICall(output);
-    masm.loadJSContext(output);
-    masm.passABIArg(output);
-    masm.passABIArg(object);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, RegExpPrototypeOptimizableRaw));
-    masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-    masm.load8ZeroExtend(Address(masm.getStackPointer(), 0), output);
-    masm.freeStack(sizeof(void*));
-
-    restoreVolatile(output);
-
-    masm.jump(ool->rejoin());
-}
-
-class OutOfLineRegExpInstanceOptimizable : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpInstanceOptimizable* ins_;
-
-  public:
-    explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
-      : ins_(ins)
-    { }
-
-    void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpInstanceOptimizable(this);
-    }
-    LRegExpInstanceOptimizable* ins() const {
-        return ins_;
-    }
-};
-
-void
-CodeGenerator::visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
-{
-    Register object = ToRegister(ins->object());
-    Register output = ToRegister(ins->output());
-    Register temp = ToRegister(ins->temp());
-
-    OutOfLineRegExpInstanceOptimizable* ool = new(alloc()) OutOfLineRegExpInstanceOptimizable(ins);
-    addOutOfLineCode(ool, ins->mir());
-
-    masm.loadJSContext(temp);
-    masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
-    size_t offset = JSCompartment::offsetOfRegExps() +
-                    RegExpCompartment::offsetOfOptimizableRegExpInstanceShape();
-    masm.loadPtr(Address(temp, offset), temp);
-
-    masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
-    masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
-    masm.move32(Imm32(0x1), output);
-
-    masm.bind(ool->rejoin());
-}
-
-void
-CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOptimizable* ool)
-{
-    LRegExpInstanceOptimizable* ins = ool->ins();
-    Register object = ToRegister(ins->object());
-    Register proto = ToRegister(ins->proto());
-    Register output = ToRegister(ins->output());
-    Register temp = ToRegister(ins->temp());
-
-    saveVolatile(output);
-
-    masm.reserveStack(sizeof(void*));
-    masm.moveStackPtrTo(temp);
-
-    masm.setupUnalignedABICall(output);
-    masm.loadJSContext(output);
-    masm.passABIArg(output);
-    masm.passABIArg(object);
-    masm.passABIArg(proto);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, RegExpInstanceOptimizableRaw));
-    masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-    masm.load8ZeroExtend(Address(masm.getStackPointer(), 0), output);
-    masm.freeStack(sizeof(void*));
-
-    restoreVolatile(output);
-
-    masm.jump(ool->rejoin());
+typedef JSString* (*RegExpReplaceFn)(JSContext*, HandleString, HandleObject, HandleString);
+static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);
+
+void
+CodeGenerator::visitRegExpReplace(LRegExpReplace* lir)
+{
+    if (lir->replacement()->isConstant())
+        pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
+    else
+        pushArg(ToRegister(lir->replacement()));
+
+    pushArg(ToRegister(lir->pattern()));
+
+    if (lir->string()->isConstant())
+        pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
+    else
+        pushArg(ToRegister(lir->string()));
+
+    callVM(RegExpReplaceInfo, lir);
 }
 
 typedef JSString* (*StringReplaceFn)(JSContext*, HandleString, HandleString, HandleString);
 static const VMFunction StringFlatReplaceInfo = FunctionInfo<StringReplaceFn>(js::str_flat_replace_string);
 static const VMFunction StringReplaceInfo = FunctionInfo<StringReplaceFn>(StringReplace);
 
 void
 CodeGenerator::visitStringReplace(LStringReplace* lir)
@@ -7374,23 +7152,22 @@ CodeGenerator::visitSinCos(LSinCos *lir)
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED_(js::math_sincos)));
 #undef MAYBE_CACHED_
 
     masm.loadDouble(Address(masm.getStackPointer(), 0), outputCos);
     masm.loadDouble(Address(masm.getStackPointer(), sizeof(double)), outputSin);
     masm.freeStack(sizeof(double) * 2);
 }
 
-typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
+typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString);
 static const VMFunction StringSplitInfo = FunctionInfo<StringSplitFn>(js::str_split_string);
 
 void
 CodeGenerator::visitStringSplit(LStringSplit* lir)
 {
-    pushArg(Imm32(INT32_MAX));
     pushArg(ToRegister(lir->separator()));
     pushArg(ToRegister(lir->string()));
     pushArg(ImmGCPtr(lir->mir()->group()));
 
     callVM(StringSplitInfo, lir);
 }
 
 void
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -42,20 +42,17 @@ class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineCallPostWriteElementBarrier;
 class OutOfLineIsCallable;
 class OutOfLineIsConstructor;
 class OutOfLineRegExpMatcher;
-class OutOfLineRegExpSearcher;
 class OutOfLineRegExpTester;
-class OutOfLineRegExpPrototypeOptimizable;
-class OutOfLineRegExpInstanceOptimizable;
 class OutOfLineLambdaArrow;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
     ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
@@ -109,24 +106,19 @@ class CodeGenerator : public CodeGenerat
     void visitDoubleToString(LDoubleToString* lir);
     void visitValueToString(LValueToString* lir);
     void visitValueToObjectOrNull(LValueToObjectOrNull* lir);
     void visitInteger(LInteger* lir);
     void visitInteger64(LInteger64* lir);
     void visitRegExp(LRegExp* lir);
     void visitRegExpMatcher(LRegExpMatcher* lir);
     void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
-    void visitRegExpSearcher(LRegExpSearcher* lir);
-    void visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool);
     void visitRegExpTester(LRegExpTester* lir);
     void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
-    void visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* lir);
-    void visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototypeOptimizable* ool);
-    void visitRegExpInstanceOptimizable(LRegExpInstanceOptimizable* lir);
-    void visitOutOfLineRegExpInstanceOptimizable(OutOfLineRegExpInstanceOptimizable* ool);
+    void visitRegExpReplace(LRegExpReplace* lir);
     void visitStringReplace(LStringReplace* lir);
     void emitSharedStub(ICStub::Kind kind, LInstruction* lir);
     void visitBinarySharedStub(LBinarySharedStub* lir);
     void visitUnarySharedStub(LUnarySharedStub* lir);
     void visitNullarySharedStub(LNullarySharedStub* lir);
     void visitLambda(LLambda* lir);
     void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool);
     void visitLambdaArrow(LLambdaArrow* lir);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -60,29 +60,25 @@
     _(MathASinH)                    \
     _(MathATanH)                    \
     _(MathACosH)                    \
     _(MathSign)                     \
     _(MathTrunc)                    \
     _(MathCbrt)                     \
                                     \
     _(RegExpMatcher)                \
-    _(RegExpSearcher)               \
     _(RegExpTester)                 \
     _(IsRegExpObject)               \
-    _(RegExpPrototypeOptimizable)   \
-    _(RegExpInstanceOptimizable)    \
                                     \
     _(String)                       \
+    _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
-                                    \
-    _(IntrinsicStringReplaceString) \
-    _(IntrinsicStringSplitString)   \
+    _(StringReplace)                \
                                     \
     _(ObjectCreate)                 \
                                     \
     _(SimdInt32x4)                  \
     _(SimdUint32x4)                 \
     _(SimdFloat32x4)                \
     _(SimdBool32x4)                 \
                                     \
@@ -102,17 +98,16 @@
     _(IntrinsicToObject)            \
     _(IntrinsicIsObject)            \
     _(IntrinsicIsWrappedArrayConstructor) \
     _(IntrinsicToInteger)           \
     _(IntrinsicToString)            \
     _(IntrinsicIsConstructing)      \
     _(IntrinsicSubstringKernel)     \
     _(IntrinsicDefineDataProperty)  \
-    _(IntrinsicObjectHasPrototype)  \
                                     \
     _(IntrinsicIsArrayIterator)     \
     _(IntrinsicIsMapIterator)       \
     _(IntrinsicIsStringIterator)    \
     _(IntrinsicIsListIterator)      \
                                     \
     _(IntrinsicGetNextMapEntryForIterator) \
                                     \
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -397,17 +397,16 @@ JitRuntime::patchIonBackedges(JSRuntime*
 
 JitCompartment::JitCompartment()
   : stubCodes_(nullptr),
     cacheIRStubCodes_(nullptr),
     baselineGetPropReturnAddr_(nullptr),
     baselineSetPropReturnAddr_(nullptr),
     stringConcatStub_(nullptr),
     regExpMatcherStub_(nullptr),
-    regExpSearcherStub_(nullptr),
     regExpTesterStub_(nullptr)
 {
     baselineCallReturnAddrs_[0] = baselineCallReturnAddrs_[1] = nullptr;
 }
 
 JitCompartment::~JitCompartment()
 {
     js_delete(stubCodes_);
@@ -659,36 +658,31 @@ JitCompartment::sweep(FreeOp* fop, JSCom
         baselineSetPropReturnAddr_ = nullptr;
 
     if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_))
         stringConcatStub_ = nullptr;
 
     if (regExpMatcherStub_ && !IsMarkedUnbarriered(&regExpMatcherStub_))
         regExpMatcherStub_ = nullptr;
 
-    if (regExpSearcherStub_ && !IsMarkedUnbarriered(&regExpSearcherStub_))
-        regExpSearcherStub_ = nullptr;
-
     if (regExpTesterStub_ && !IsMarkedUnbarriered(&regExpTesterStub_))
         regExpTesterStub_ = nullptr;
 
     for (ReadBarrieredObject& obj : simdTemplateObjects_) {
         if (obj && IsAboutToBeFinalized(&obj))
             obj.set(nullptr);
     }
 }
 
 void
 JitCompartment::toggleBarriers(bool enabled)
 {
     // Toggle barriers in compartment wide stubs that have patchable pre barriers.
     if (regExpMatcherStub_)
         regExpMatcherStub_->togglePreBarriers(enabled, Reprotect);
-    if (regExpSearcherStub_)
-        regExpSearcherStub_->togglePreBarriers(enabled, Reprotect);
     if (regExpTesterStub_)
         regExpTesterStub_->togglePreBarriers(enabled, Reprotect);
 
     // Toggle barriers in baseline IC stubs.
     for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
         JitCode* code = *e.front().value().unsafeGet();
         code->togglePreBarriers(enabled, Reprotect);
     }
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1846,17 +1846,17 @@ jit::MakeMRegExpHoistable(MIRGraph& grap
                 // No DCE or GVN or something has happened.
                 if (i->consumer()->isResumePoint())
                     continue;
 
                 MOZ_ASSERT(i->consumer()->isDefinition());
 
                 // All MRegExp* MIR's don't adjust the regexp.
                 MDefinition* use = i->consumer()->toDefinition();
-                if (use->isRegExpMatcher() || use->isRegExpTester() || use->isRegExpSearcher())
+                if (use->isRegExpReplace())
                     continue;
 
                 hoistable = false;
                 break;
             }
 
             if (!hoistable)
                 continue;
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -820,33 +820,28 @@ class IonBuilder
     InliningStatus inlineMathPow(CallInfo& callInfo);
     InliningStatus inlineMathRandom(CallInfo& callInfo);
     InliningStatus inlineMathImul(CallInfo& callInfo);
     InliningStatus inlineMathFRound(CallInfo& callInfo);
     InliningStatus inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);
 
     // String natives.
     InliningStatus inlineStringObject(CallInfo& callInfo);
+    InliningStatus inlineConstantStringSplit(CallInfo& callInfo);
+    InliningStatus inlineStringSplit(CallInfo& callInfo);
     InliningStatus inlineStrCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineConstantCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
     InliningStatus inlineStrCharAt(CallInfo& callInfo);
-
-    // String intrinsics.
-    InliningStatus inlineStringReplaceString(CallInfo& callInfo);
-    InliningStatus inlineConstantStringSplitString(CallInfo& callInfo);
-    InliningStatus inlineStringSplitString(CallInfo& callInfo);
+    InliningStatus inlineStrReplace(CallInfo& callInfo);
 
     // RegExp intrinsics.
     InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
-    InliningStatus inlineRegExpSearcher(CallInfo& callInfo);
     InliningStatus inlineRegExpTester(CallInfo& callInfo);
     InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
-    InliningStatus inlineRegExpPrototypeOptimizable(CallInfo& callInfo);
-    InliningStatus inlineRegExpInstanceOptimizable(CallInfo& callInfo);
 
     // Object natives and intrinsics.
     InliningStatus inlineObjectCreate(CallInfo& callInfo);
     InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
 
     // Atomics natives.
     InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
     InliningStatus inlineAtomicsExchange(CallInfo& callInfo);
@@ -933,17 +928,16 @@ class IonBuilder
     InliningStatus inlineToString(CallInfo& callInfo);
     InliningStatus inlineDump(CallInfo& callInfo);
     InliningStatus inlineHasClass(CallInfo& callInfo, const Class* clasp,
                                   const Class* clasp2 = nullptr,
                                   const Class* clasp3 = nullptr,
                                   const Class* clasp4 = nullptr);
     InliningStatus inlineIsConstructing(CallInfo& callInfo);
     InliningStatus inlineSubstringKernel(CallInfo& callInfo);
-    InliningStatus inlineObjectHasPrototype(CallInfo& callInfo);
 
     // Testing functions.
     InliningStatus inlineBailout(CallInfo& callInfo);
     InliningStatus inlineAssertFloat32(CallInfo& callInfo);
     InliningStatus inlineAssertRecoveredOnBailout(CallInfo& callInfo);
 
     // Bind function.
     InliningStatus inlineBoundFunction(CallInfo& callInfo, JSFunction* target);
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -441,24 +441,22 @@ class JitCompartment
     // Stubs to concatenate two strings inline, or perform RegExp calls inline.
     // These bake in zone and compartment specific pointers and can't be stored
     // in JitRuntime. These are weak pointers, but are not declared as
     // ReadBarriered since they are only read from during Ion compilation,
     // which may occur off thread and whose barriers are captured during
     // CodeGenerator::link.
     JitCode* stringConcatStub_;
     JitCode* regExpMatcherStub_;
-    JitCode* regExpSearcherStub_;
     JitCode* regExpTesterStub_;
 
     mozilla::EnumeratedArray<SimdType, SimdType::Count, ReadBarrieredObject> simdTemplateObjects_;
 
     JitCode* generateStringConcatStub(JSContext* cx);
     JitCode* generateRegExpMatcherStub(JSContext* cx);
-    JitCode* generateRegExpSearcherStub(JSContext* cx);
     JitCode* generateRegExpTesterStub(JSContext* cx);
 
   public:
     JSObject* getSimdTemplateObjectFor(JSContext* cx, Handle<SimdTypeDescr*> descr) {
         ReadBarrieredObject& tpl = simdTemplateObjects_[descr->type()];
         if (!tpl)
             tpl.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap));
         return tpl.get();
@@ -560,27 +558,16 @@ class JitCompartment
 
     bool ensureRegExpMatcherStubExists(JSContext* cx) {
         if (regExpMatcherStub_)
             return true;
         regExpMatcherStub_ = generateRegExpMatcherStub(cx);
         return regExpMatcherStub_ != nullptr;
     }
 
-    JitCode* regExpSearcherStubNoBarrier() const {
-        return regExpSearcherStub_;
-    }
-
-    bool ensureRegExpSearcherStubExists(JSContext* cx) {
-        if (regExpSearcherStub_)
-            return true;
-        regExpSearcherStub_ = generateRegExpSearcherStub(cx);
-        return regExpSearcherStub_ != nullptr;
-    }
-
     JitCode* regExpTesterStubNoBarrier() const {
         return regExpTesterStub_;
     }
 
     bool ensureRegExpTesterStubExists(JSContext* cx) {
         if (regExpTesterStub_)
             return true;
         regExpTesterStub_ = generateRegExpTesterStub(cx);
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2182,16 +2182,25 @@ MustCloneRegExpForCall(MCall* call, uint
 {
     // We have a regex literal flowing into a call. Return |false| iff
     // this is a native call that does not let the regex escape.
 
     JSFunction* target = call->getSingleTarget();
     if (!target || !target->isNative())
         return true;
 
+    if (useIndex == MCall::IndexOfArgument(0) &&
+        (target->native() == str_split ||
+         target->native() == str_replace ||
+         target->native() == str_match ||
+         target->native() == str_search))
+    {
+        return false;
+    }
+
     return true;
 }
 
 
 static bool
 MustCloneRegExp(MRegExp* regexp)
 {
     if (regexp->mustClone())
@@ -2208,23 +2217,16 @@ MustCloneRegExp(MRegExp* regexp)
         MDefinition* def = node->toDefinition();
         if (def->isRegExpMatcher()) {
             MRegExpMatcher* test = def->toRegExpMatcher();
             if (test->indexOf(*iter) == 1) {
                 // Optimized RegExp.prototype.exec.
                 MOZ_ASSERT(test->regexp() == regexp);
                 continue;
             }
-        } else if (def->isRegExpSearcher()) {
-            MRegExpSearcher* test = def->toRegExpSearcher();
-            if (test->indexOf(*iter) == 1) {
-                // Optimized RegExp.prototype.exec.
-                MOZ_ASSERT(test->regexp() == regexp);
-                continue;
-            }
         } else if (def->isRegExpTester()) {
             MRegExpTester* test = def->toRegExpTester();
             if (test->indexOf(*iter) == 1) {
                 // Optimized RegExp.prototype.test.
                 MOZ_ASSERT(test->regexp() == regexp);
                 continue;
             }
         } else if (def->isCall()) {
@@ -2252,72 +2254,54 @@ LIRGenerator::visitRegExp(MRegExp* ins)
 }
 
 void
 LIRGenerator::visitRegExpMatcher(MRegExpMatcher* ins)
 {
     MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
     MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
 
     LRegExpMatcher* lir = new(alloc()) LRegExpMatcher(useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg),
                                                       useFixedAtStart(ins->string(), RegExpMatcherStringReg),
-                                                      useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg));
-    defineReturn(lir, ins);
-    assignSafepoint(lir, ins);
-}
-
-void
-LIRGenerator::visitRegExpSearcher(MRegExpSearcher* ins)
-{
-    MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
-    MOZ_ASSERT(ins->string()->type() == MIRType_String);
-    MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
-
-    LRegExpSearcher* lir = new(alloc()) LRegExpSearcher(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg),
-                                                        useFixedAtStart(ins->string(), RegExpTesterStringReg),
-                                                        useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg));
+                                                      useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg),
+                                                      useFixedAtStart(ins->sticky(), RegExpMatcherStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitRegExpTester(MRegExpTester* ins)
 {
     MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
     MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
 
     LRegExpTester* lir = new(alloc()) LRegExpTester(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg),
                                                     useFixedAtStart(ins->string(), RegExpTesterStringReg),
-                                                    useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg));
+                                                    useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg),
+                                                    useFixedAtStart(ins->sticky(), RegExpTesterStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
-void
-LIRGenerator::visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType_Object);
-    MOZ_ASSERT(ins->type() == MIRType_Boolean);
-    LRegExpPrototypeOptimizable* lir = new(alloc()) LRegExpPrototypeOptimizable(useRegister(ins->object()),
-                                                                                temp());
-    define(lir, ins);
-}
-
-void
-LIRGenerator::visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins)
-{
-    MOZ_ASSERT(ins->object()->type() == MIRType_Object);
-    MOZ_ASSERT(ins->proto()->type() == MIRType_Object);
-    MOZ_ASSERT(ins->type() == MIRType_Boolean);
-    LRegExpInstanceOptimizable* lir = new(alloc()) LRegExpInstanceOptimizable(useRegister(ins->object()),
-                                                                              useRegister(ins->proto()),
-                                                                              temp());
-    define(lir, ins);
+void 
+LIRGenerator::visitRegExpReplace(MRegExpReplace* ins)
+{
+    MOZ_ASSERT(ins->pattern()->type() == MIRType_Object);
+    MOZ_ASSERT(ins->string()->type() == MIRType_String);
+    MOZ_ASSERT(ins->replacement()->type() == MIRType_String);
+
+    LRegExpReplace* lir = new(alloc()) LRegExpReplace(useRegisterOrConstantAtStart(ins->string()),
+                                                      useRegisterAtStart(ins->pattern()),
+                                                      useRegisterOrConstantAtStart(ins->replacement()));
+    defineReturn(lir, ins);
+    assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitStringReplace(MStringReplace* ins)
 {
     MOZ_ASSERT(ins->pattern()->type() == MIRType_String);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
     MOZ_ASSERT(ins->replacement()->type() == MIRType_String);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -159,20 +159,18 @@ class LIRGenerator : public LIRGenerator
     void visitToInt32(MToInt32* convert);
     void visitTruncateToInt32(MTruncateToInt32* truncate);
     void visitWrapInt64ToInt32(MWrapInt64ToInt32* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
     void visitToString(MToString* convert);
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
     void visitRegExpMatcher(MRegExpMatcher* ins);
-    void visitRegExpSearcher(MRegExpSearcher* ins);
     void visitRegExpTester(MRegExpTester* ins);
-    void visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins);
-    void visitRegExpInstanceOptimizable(MRegExpInstanceOptimizable* ins);
+    void visitRegExpReplace(MRegExpReplace* ins);
     void visitStringReplace(MStringReplace* ins);
     void visitBinarySharedStub(MBinarySharedStub* ins);
     void visitUnarySharedStub(MUnarySharedStub* ins);
     void visitNullarySharedStub(MNullarySharedStub* ins);
     void visitLambda(MLambda* ins);
     void visitLambdaArrow(MLambdaArrow* ins);
     void visitKeepAliveObject(MKeepAliveObject* ins);
     void visitSlots(MSlots* ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1,31 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsmath.h"
-#include "jsobj.h"
-#include "jsstr.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/SIMD.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ProxyObject.h"
-#include "vm/SelfHosting.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/StringObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
@@ -173,42 +170,34 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::MathTrunc:
         return inlineMathFunction(callInfo, MMathFunction::Trunc);
       case InlinableNative::MathCbrt:
         return inlineMathFunction(callInfo, MMathFunction::Cbrt);
 
       // RegExp natives.
       case InlinableNative::RegExpMatcher:
         return inlineRegExpMatcher(callInfo);
-      case InlinableNative::RegExpSearcher:
-        return inlineRegExpSearcher(callInfo);
       case InlinableNative::RegExpTester:
         return inlineRegExpTester(callInfo);
       case InlinableNative::IsRegExpObject:
         return inlineIsRegExpObject(callInfo);
-      case InlinableNative::RegExpPrototypeOptimizable:
-        return inlineRegExpPrototypeOptimizable(callInfo);
-      case InlinableNative::RegExpInstanceOptimizable:
-        return inlineRegExpInstanceOptimizable(callInfo);
 
       // String natives.
       case InlinableNative::String:
         return inlineStringObject(callInfo);
+      case InlinableNative::StringSplit:
+        return inlineStringSplit(callInfo);
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
       case InlinableNative::StringFromCharCode:
         return inlineStrFromCharCode(callInfo);
       case InlinableNative::StringCharAt:
         return inlineStrCharAt(callInfo);
-
-      // String intrinsics.
-      case InlinableNative::IntrinsicStringReplaceString:
-        return inlineStringReplaceString(callInfo);
-      case InlinableNative::IntrinsicStringSplitString:
-        return inlineStringSplitString(callInfo);
+      case InlinableNative::StringReplace:
+        return inlineStrReplace(callInfo);
 
       // Object natives.
       case InlinableNative::ObjectCreate:
         return inlineObjectCreate(callInfo);
 
       // SIMD natives.
       case InlinableNative::SimdInt32x4:
         return inlineSimd(callInfo, target, SimdType::Int32x4);
@@ -265,18 +254,16 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::IntrinsicIsMapIterator:
         return inlineHasClass(callInfo, &MapIteratorObject::class_);
       case InlinableNative::IntrinsicIsStringIterator:
         return inlineHasClass(callInfo, &StringIteratorObject::class_);
       case InlinableNative::IntrinsicIsListIterator:
         return inlineHasClass(callInfo, &ListIteratorObject::class_);
       case InlinableNative::IntrinsicDefineDataProperty:
         return inlineDefineDataProperty(callInfo);
-      case InlinableNative::IntrinsicObjectHasPrototype:
-        return inlineObjectHasPrototype(callInfo);
 
       // Map intrinsics.
       case InlinableNative::IntrinsicGetNextMapEntryForIterator:
         return inlineGetNextMapEntryForIterator(callInfo);
 
       // ArrayBuffer intrinsics.
       case InlinableNative::IntrinsicArrayBufferByteLength:
         return inlineArrayBufferByteLength(callInfo);
@@ -1379,47 +1366,47 @@ IonBuilder::inlineStringObject(CallInfo&
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
+IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
 {
+    if (!callInfo.thisArg()->isConstant())
+        return InliningStatus_NotInlined;
+
     if (!callInfo.getArg(0)->isConstant())
         return InliningStatus_NotInlined;
 
-    if (!callInfo.getArg(1)->isConstant())
+    MConstant* argval = callInfo.getArg(0)->toConstant();
+    if (argval->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
-    MConstant* strval = callInfo.getArg(0)->toConstant();
-    if (strval->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    MConstant* sepval = callInfo.getArg(1)->toConstant();
+    MConstant* strval = callInfo.thisArg()->toConstant();
     if (strval->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
     // Check if exist a template object in stub.
-    JSString* stringStr = nullptr;
-    JSString* stringSep = nullptr;
+    JSString* stringThis = nullptr;
+    JSString* stringArg = nullptr;
     JSObject* templateObject = nullptr;
-    if (!inspector->isOptimizableCallStringSplit(pc, &stringStr, &stringSep, &templateObject))
+    if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject))
         return InliningStatus_NotInlined;
 
-    MOZ_ASSERT(stringStr);
-    MOZ_ASSERT(stringSep);
+    MOZ_ASSERT(stringThis);
+    MOZ_ASSERT(stringArg);
     MOZ_ASSERT(templateObject);
 
-    if (strval->toString() != stringStr)
+    if (strval->toString() != stringThis)
         return InliningStatus_NotInlined;
 
-    if (sepval->toString() != stringSep)
+    if (argval->toString() != stringArg)
         return InliningStatus_NotInlined;
 
     // Check if |templateObject| is valid.
     TypeSet::ObjectKey* retType = TypeSet::ObjectKey::get(templateObject);
     if (retType->unknownProperties())
         return InliningStatus_NotInlined;
 
     HeapTypeSetKey key = retType->property(JSID_VOID);
@@ -1480,37 +1467,33 @@ IonBuilder::inlineConstantStringSplitStr
     MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
     if (!resumeAfter(setLength))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineStringSplitString(CallInfo& callInfo)
+IonBuilder::inlineStringSplit(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
+    if (callInfo.argc() != 1 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    MDefinition* strArg = callInfo.getArg(0);
-    MDefinition* sepArg = callInfo.getArg(1);
-
-    if (strArg->type() != MIRType_String)
+    if (callInfo.thisArg()->type() != MIRType_String)
         return InliningStatus_NotInlined;
-
-    if (sepArg->type() != MIRType_String)
+    if (callInfo.getArg(0)->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
-    IonBuilder::InliningStatus resultConstStringSplit = inlineConstantStringSplitString(callInfo);
+    IonBuilder::InliningStatus resultConstStringSplit = inlineConstantStringSplit(callInfo);
     if (resultConstStringSplit != InliningStatus_NotInlined)
         return resultConstStringSplit;
 
-    JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::intrinsic_StringSplitString);
+    JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::str_split);
     if (!templateObject)
         return InliningStatus_NotInlined;
 
     TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(templateObject);
     if (retKey->unknownProperties())
         return InliningStatus_NotInlined;
 
     HeapTypeSetKey key = retKey->property(JSID_VOID);
@@ -1522,77 +1505,25 @@ IonBuilder::inlineStringSplitString(Call
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
     MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject),
                                                   constraints());
     current->add(templateObjectDef);
 
-    MStringSplit* ins = MStringSplit::New(alloc(), constraints(), strArg, sepArg,
-                                          templateObjectDef);
+    MStringSplit* ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
+                                          callInfo.getArg(0), templateObjectDef);
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineObjectHasPrototype(CallInfo& callInfo)
-{
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
-    MDefinition* objArg = callInfo.getArg(0);
-    MDefinition* protoArg = callInfo.getArg(1);
-
-    if (objArg->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-    if (protoArg->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-
-    // Inline only when both obj and proto are singleton objects and
-    // obj does not have uncacheable proto and obj.__proto__ is proto.
-    TemporaryTypeSet* objTypes = objArg->resultTypeSet();
-    if (!objTypes || objTypes->unknownObject() || objTypes->getObjectCount() != 1)
-        return InliningStatus_NotInlined;
-
-    TypeSet::ObjectKey* objKey = objTypes->getObject(0);
-    if (!objKey || !objKey->hasStableClassAndProto(constraints()))
-        return InliningStatus_NotInlined;
-    if (!objKey->isSingleton() || !objKey->singleton()->is<NativeObject>())
-        return InliningStatus_NotInlined;
-
-    JSObject* obj = &objKey->singleton()->as<NativeObject>();
-    if (obj->hasUncacheableProto())
-        return InliningStatus_NotInlined;
-
-    JSObject* actualProto = checkNurseryObject(objKey->proto().toObjectOrNull());
-    if (actualProto == nullptr)
-        return InliningStatus_NotInlined;
-
-    TemporaryTypeSet* protoTypes = protoArg->resultTypeSet();
-    if (!protoTypes || protoTypes->unknownObject() || protoTypes->getObjectCount() != 1)
-        return InliningStatus_NotInlined;
-
-    TypeSet::ObjectKey* protoKey = protoTypes->getObject(0);
-    if (!protoKey || !protoKey->hasStableClassAndProto(constraints()))
-        return InliningStatus_NotInlined;
-    if (!protoKey->isSingleton() || !protoKey->singleton()->is<NativeObject>())
-        return InliningStatus_NotInlined;
-
-    JSObject* proto = &protoKey->singleton()->as<NativeObject>();
-    pushConstant(BooleanValue(proto == actualProto));
-    callInfo.setImplicitlyUsedUnchecked();
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
 IonBuilder::inlineStrCharCodeAt(CallInfo& callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Int32)
@@ -1722,148 +1653,106 @@ IonBuilder::inlineStrCharAt(CallInfo& ca
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
 {
     // This is called from Self-hosted JS, after testing each argument,
     // most of following tests should be passed.
 
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
+    if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    MDefinition* rxArg = callInfo.getArg(0);
-    MDefinition* strArg = callInfo.getArg(1);
-    MDefinition* lastIndexArg = callInfo.getArg(2);
-
-    if (rxArg->type() != MIRType_Object)
+    // regexp
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
-    const Class* clasp = rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
+    TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (strArg->mightBeType(MIRType_Object))
+    // string
+    if (callInfo.getArg(1)->mightBeType(MIRType_Object))
         return InliningStatus_NotInlined;
 
-    if (lastIndexArg->type() != MIRType_Int32)
+    // lastIndex: Only inline if it's Int32.
+    if (callInfo.getArg(2)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    // sticky
+    if (callInfo.getArg(3)->type() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
     JSContext* cx = GetJitContext()->cx;
     if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* matcher = MRegExpMatcher::New(alloc(), rxArg, strArg, lastIndexArg);
+    MInstruction* matcher = MRegExpMatcher::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
+                                                callInfo.getArg(2), callInfo.getArg(3));
     current->add(matcher);
     current->push(matcher);
 
     if (!resumeAfter(matcher))
         return InliningStatus_Error;
 
     if (!pushTypeBarrier(matcher, getInlineReturnTypeSet(), BarrierKind::TypeSet))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineRegExpSearcher(CallInfo& callInfo)
-{
-    // This is called from Self-hosted JS, after testing each argument,
-    // most of following tests should be passed.
-
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
-    MDefinition* rxArg = callInfo.getArg(0);
-    MDefinition* strArg = callInfo.getArg(1);
-    MDefinition* lastIndexArg = callInfo.getArg(2);
-
-    if (rxArg->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-
-    TemporaryTypeSet* regexpTypes = rxArg->resultTypeSet();
-    const Class* clasp = regexpTypes ? regexpTypes->getKnownClass(constraints()) : nullptr;
-    if (clasp != &RegExpObject::class_)
-        return InliningStatus_NotInlined;
-
-    if (strArg->mightBeType(MIRType_Object))
-        return InliningStatus_NotInlined;
-
-    if (lastIndexArg->type() != MIRType_Int32)
-        return InliningStatus_NotInlined;
-
-    JSContext* cx = GetJitContext()->cx;
-    if (!cx->compartment()->jitCompartment()->ensureRegExpSearcherStubExists(cx)) {
-        cx->clearPendingException(); // OOM or overrecursion.
-        return InliningStatus_Error;
-    }
-
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MInstruction* searcher = MRegExpSearcher::New(alloc(), rxArg, strArg, lastIndexArg);
-    current->add(searcher);
-    current->push(searcher);
-
-    if (!resumeAfter(searcher))
-        return InliningStatus_Error;
-
-    if (!pushTypeBarrier(searcher, getInlineReturnTypeSet(), BarrierKind::TypeSet))
-        return InliningStatus_Error;
-
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
 IonBuilder::inlineRegExpTester(CallInfo& callInfo)
 {
     // This is called from Self-hosted JS, after testing each argument,
     // most of following tests should be passed.
 
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
+    if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    MDefinition* rxArg = callInfo.getArg(0);
-    MDefinition* strArg = callInfo.getArg(1);
-    MDefinition* lastIndexArg = callInfo.getArg(2);
-
-    if (rxArg->type() != MIRType_Object)
+    // regexp
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
-    const Class* clasp = rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
+    TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (strArg->mightBeType(MIRType_Object))
+    // string
+    if (callInfo.getArg(1)->mightBeType(MIRType_Object))
         return InliningStatus_NotInlined;
 
-    if (lastIndexArg->type() != MIRType_Int32)
+    // lastIndex: Only inline if it's Int32.
+    if (callInfo.getArg(2)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    // sticky
+    if (callInfo.getArg(3)->type() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
     JSContext* cx = GetJitContext()->cx;
     if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* tester = MRegExpTester::New(alloc(), rxArg, strArg, lastIndexArg);
+    MInstruction* tester = MRegExpTester::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
+                                              callInfo.getArg(2), callInfo.getArg(3));
     current->add(tester);
     current->push(tester);
 
     if (!resumeAfter(tester))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
@@ -1898,96 +1787,51 @@ IonBuilder::inlineIsRegExpObject(CallInf
 
     pushConstant(BooleanValue(isRegExpObject));
 
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineRegExpPrototypeOptimizable(CallInfo& callInfo)
-{
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
-    MDefinition* protoArg = callInfo.getArg(0);
-
-    if (protoArg->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-
-    if (getInlineReturnType() != MIRType_Boolean)
-        return InliningStatus_NotInlined;
-
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MInstruction* opt = MRegExpPrototypeOptimizable::New(alloc(), protoArg);
-    current->add(opt);
-    current->push(opt);
-
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
-IonBuilder::inlineRegExpInstanceOptimizable(CallInfo& callInfo)
+IonBuilder::inlineStrReplace(CallInfo& callInfo)
 {
     if (callInfo.argc() != 2 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    MDefinition* rxArg = callInfo.getArg(0);
-    MDefinition* protoArg = callInfo.getArg(1);
-
-    if (rxArg->type() != MIRType_Object)
+    // Return: String.
+    if (getInlineReturnType() != MIRType_String)
+        return InliningStatus_NotInlined;
+
+    // This: String.
+    if (callInfo.thisArg()->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
-    if (protoArg->type() != MIRType_Object)
+    // Arg 0: RegExp.
+    TemporaryTypeSet* arg0Type = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Type ? arg0Type->getKnownClass(constraints()) : nullptr;
+    if (clasp != &RegExpObject::class_ && callInfo.getArg(0)->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
-    if (getInlineReturnType() != MIRType_Boolean)
+    // Arg 1: String.
+    if (callInfo.getArg(1)->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* opt = MRegExpInstanceOptimizable::New(alloc(), rxArg, protoArg);
-    current->add(opt);
-    current->push(opt);
-
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
-IonBuilder::inlineStringReplaceString(CallInfo& callInfo)
-{
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
+    MInstruction* cte;
+    if (callInfo.getArg(0)->type() == MIRType_String) {
+        cte = MStringReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0),
+                                  callInfo.getArg(1));
+    } else {
+        cte = MRegExpReplace::New(alloc(), callInfo.thisArg(), callInfo.getArg(0),
+                                  callInfo.getArg(1));
     }
-
-    if (getInlineReturnType() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    MDefinition* strArg = callInfo.getArg(0);
-    MDefinition* patArg = callInfo.getArg(1);
-    MDefinition* replArg = callInfo.getArg(2);
-
-    if (strArg->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    if (patArg->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    if (replArg->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MInstruction* cte = MStringReplace::New(alloc(), strArg, patArg, replArg);
     current->add(cte);
     current->push(cte);
     if (cte->isEffectful() && !resumeAfter(cte))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7896,227 +7896,194 @@ class MRegExp : public MNullaryInstructi
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MRegExpMatcher
-  : public MAryInstruction<3>,
-    public Mix3Policy<ObjectPolicy<0>,
+  : public MAryInstruction<4>,
+    public Mix4Policy<ObjectPolicy<0>,
                       StringPolicy<1>,
-                      IntPolicy<2> >::Data
+                      IntPolicy<2>,
+                      BooleanPolicy<3> >::Data
 {
   private:
 
-    MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
-      : MAryInstruction<3>()
+    MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
+                   MDefinition* sticky)
+      : MAryInstruction<4>()
     {
         initOperand(0, regexp);
         initOperand(1, string);
         initOperand(2, lastIndex);
+        initOperand(3, sticky);
 
         setMovable();
         // May be object or null.
         setResultType(MIRType_Value);
     }
 
   public:
     INSTRUCTION_HEADER(RegExpMatcher)
 
     static MRegExpMatcher* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
-                               MDefinition* lastIndex)
-    {
-        return new(alloc) MRegExpMatcher(regexp, string, lastIndex);
+                               MDefinition* lastIndex, MDefinition* sticky)
+    {
+        return new(alloc) MRegExpMatcher(regexp, string, lastIndex, sticky);
     }
 
     MDefinition* regexp() const {
         return getOperand(0);
     }
     MDefinition* string() const {
         return getOperand(1);
     }
     MDefinition* lastIndex() const {
         return getOperand(2);
     }
-
-    bool writeRecoverData(CompactBufferWriter& writer) const override;
-
-    bool canRecoverOnBailout() const override {
-        return true;
-    }
-
-    bool possiblyCalls() const override {
-        return true;
-    }
-};
-
-class MRegExpSearcher
-  : public MAryInstruction<3>,
-    public Mix3Policy<ObjectPolicy<0>,
-                      StringPolicy<1>,
-                      IntPolicy<2> >::Data
-{
-  private:
-
-    MRegExpSearcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
-      : MAryInstruction<3>()
-    {
-        initOperand(0, regexp);
-        initOperand(1, string);
-        initOperand(2, lastIndex);
-
-        setMovable();
-        setResultType(MIRType_Int32);
-    }
-
-  public:
-    INSTRUCTION_HEADER(RegExpSearcher)
-
-    static MRegExpSearcher* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
-                                MDefinition* lastIndex)
-    {
-        return new(alloc) MRegExpSearcher(regexp, string, lastIndex);
-    }
-
-    MDefinition* regexp() const {
-        return getOperand(0);
-    }
-    MDefinition* string() const {
-        return getOperand(1);
-    }
-    MDefinition* lastIndex() const {
-        return getOperand(2);
+    MDefinition* sticky() const {
+        return getOperand(3);
     }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
 
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MRegExpTester
-  : public MAryInstruction<3>,
-    public Mix3Policy<ObjectPolicy<0>,
+  : public MAryInstruction<4>,
+    public Mix4Policy<ObjectPolicy<0>,
                       StringPolicy<1>,
-                      IntPolicy<2> >::Data
+                      IntPolicy<2>,
+                      BooleanPolicy<3> >::Data
 {
   private:
 
-    MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex)
-      : MAryInstruction<3>()
+    MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
+                  MDefinition* sticky)
+      : MAryInstruction<4>()
     {
         initOperand(0, regexp);
         initOperand(1, string);
         initOperand(2, lastIndex);
+        initOperand(3, sticky);
 
         setMovable();
         setResultType(MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(RegExpTester)
 
     static MRegExpTester* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
-                              MDefinition* lastIndex)
-    {
-        return new(alloc) MRegExpTester(regexp, string, lastIndex);
+                              MDefinition* lastIndex, MDefinition* sticky)
+    {
+        return new(alloc) MRegExpTester(regexp, string, lastIndex, sticky);
     }
 
     MDefinition* regexp() const {
         return getOperand(0);
     }
     MDefinition* string() const {
         return getOperand(1);
     }
     MDefinition* lastIndex() const {
         return getOperand(2);
     }
+    MDefinition* sticky() const {
+        return getOperand(3);
+    }
 
     bool possiblyCalls() const override {
         return true;
     }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 };
 
-class MRegExpPrototypeOptimizable
-  : public MUnaryInstruction,
-    public SingleObjectPolicy::Data
-{
-    explicit MRegExpPrototypeOptimizable(MDefinition* object)
-      : MUnaryInstruction(object)
-    {
-        setResultType(MIRType_Boolean);
-    }
-
-  public:
-    INSTRUCTION_HEADER(RegExpPrototypeOptimizable)
-
-    static MRegExpPrototypeOptimizable* New(TempAllocator& alloc, MDefinition* obj) {
-        return new(alloc) MRegExpPrototypeOptimizable(obj);
-    }
-    MDefinition* object() const {
-        return getOperand(0);
-    }
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
-    }
-};
-
-class MRegExpInstanceOptimizable
-  : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
-{
-    explicit MRegExpInstanceOptimizable(MDefinition* object, MDefinition* proto)
-      : MBinaryInstruction(object, proto)
-    {
-        setResultType(MIRType_Boolean);
-    }
-
-  public:
-    INSTRUCTION_HEADER(RegExpInstanceOptimizable)
-
-    static MRegExpInstanceOptimizable* New(TempAllocator& alloc, MDefinition* obj,
-                                           MDefinition* proto) {
-        return new(alloc) MRegExpInstanceOptimizable(obj, proto);
-    }
-    MDefinition* object() const {
-        return getOperand(0);
-    }
-    MDefinition* proto() const {
+template <class Policy1>
+class MStrReplace
+  : public MTernaryInstruction,
+    public Mix3Policy<StringPolicy<0>, Policy1, StringPolicy<2> >::Data
+{
+  protected:
+
+    MStrReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
+      : MTernaryInstruction(string, pattern, replacement)
+    {
+        setMovable();
+        setResultType(MIRType_String);
+    }
+
+  public:
+
+    MDefinition* string() const {
+        return getOperand(0);
+    }
+    MDefinition* pattern() const {
         return getOperand(1);
     }
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
+    MDefinition* replacement() const {
+        return getOperand(2);
+    }
+
+    bool possiblyCalls() const override {
+        return true;
+    }
+};
+
+class MRegExpReplace
+  : public MStrReplace< ObjectPolicy<1> >
+{
+  private:
+
+    MRegExpReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
+      : MStrReplace< ObjectPolicy<1> >(string, pattern, replacement)
+    {
+    }
+
+  public:
+    INSTRUCTION_HEADER(RegExpReplace)
+
+    static MRegExpReplace* New(TempAllocator& alloc, MDefinition* string, MDefinition* pattern, MDefinition* replacement) {
+        return new(alloc) MRegExpReplace(string, pattern, replacement);
+    }
+
+    bool writeRecoverData(CompactBufferWriter& writer) const override;
+    bool canRecoverOnBailout() const override {
+        // RegExpReplace will zero the lastIndex field when global flag is set.
+        // So we can only remove this if it's non-global.
+        // XXX: always return false for now, to work around bug 1132128.
+        if (false && pattern()->isRegExp())
+            return !pattern()->toRegExp()->source()->global();
+        return false;
     }
 };
 
 class MStringReplace
-  : public MTernaryInstruction,
-    public Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >::Data
+  : public MStrReplace< StringPolicy<1> >
 {
   private:
 
     bool isFlatReplacement_;
 
     MStringReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
-      : MTernaryInstruction(string, pattern, replacement), isFlatReplacement_(false)
-    {
-        setMovable();
-        setResultType(MIRType_String);
+      : MStrReplace< StringPolicy<1> >(string, pattern, replacement), isFlatReplacement_(false)
+    {
     }
 
   public:
     INSTRUCTION_HEADER(StringReplace)
 
     static MStringReplace* New(TempAllocator& alloc, MDefinition* string, MDefinition* pattern, MDefinition* replacement) {
         return new(alloc) MStringReplace(string, pattern, replacement);
     }
@@ -8139,36 +8106,25 @@ class MStringReplace
     }
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
-        if (isFlatReplacement_) {
+        if (isFlatReplacement_)
+        {
             MOZ_ASSERT(!pattern()->isRegExp());
             return true;
         }
+        if (pattern()->isRegExp())
+            return !pattern()->toRegExp()->source()->global();
         return false;
     }
-
-    MDefinition* string() const {
-        return getOperand(0);
-    }
-    MDefinition* pattern() const {
-        return getOperand(1);
-    }
-    MDefinition* replacement() const {
-        return getOperand(2);
-    }
-
-    bool possiblyCalls() const override {
-        return true;
-    }
 };
 
 class MSubstr
   : public MTernaryInstruction,
     public Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
 {
   private:
 
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -142,20 +142,18 @@ namespace jit {
     _(InitProp)                                                             \
     _(InitPropGetterSetter)                                                 \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(Nop)                                                                  \
     _(LimitedTruncate)                                                      \
     _(RegExp)                                                               \
     _(RegExpMatcher)                                                        \
-    _(RegExpSearcher)                                                       \
     _(RegExpTester)                                                         \
-    _(RegExpPrototypeOptimizable)                                           \
-    _(RegExpInstanceOptimizable)                                            \
+    _(RegExpReplace)                                                        \
     _(StringReplace)                                                        \
     _(Lambda)                                                               \
     _(LambdaArrow)                                                          \
     _(KeepAliveObject)                                                      \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(ConstantElements)                                                     \
     _(ConvertElementsToDoubles)                                             \
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -982,17 +982,17 @@ RStringSplit::RStringSplit(CompactBuffer
 bool
 RStringSplit::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedString str(cx, iter.read().toString());
     RootedString sep(cx, iter.read().toString());
     RootedObjectGroup group(cx, iter.read().toObject().group());
     RootedValue result(cx);
 
-    JSObject* res = str_split_string(cx, group, str, sep, INT32_MAX);
+    JSObject* res = str_split_string(cx, group, str, sep);
     if (!res)
         return false;
 
     result.setObject(*res);
     iter.storeInstructionResult(result);
     return true;
 }
 
@@ -1008,82 +1008,82 @@ RRegExpMatcher::RRegExpMatcher(CompactBu
 {}
 
 bool
 RRegExpMatcher::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedObject regexp(cx, &iter.read().toObject());
     RootedString input(cx, iter.read().toString());
     int32_t lastIndex = iter.read().toInt32();
+    bool sticky = iter.read().toBoolean();
 
     RootedValue result(cx);
-    if (!RegExpMatcherRaw(cx, regexp, input, lastIndex, nullptr, &result))
+    if (!RegExpMatcherRaw(cx, regexp, input, lastIndex, sticky, nullptr, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
-MRegExpSearcher::writeRecoverData(CompactBufferWriter& writer) const
-{
-    MOZ_ASSERT(canRecoverOnBailout());
-    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpSearcher));
-    return true;
-}
-
-RRegExpSearcher::RRegExpSearcher(CompactBufferReader& reader)
-{}
-
-bool
-RRegExpSearcher::recover(JSContext* cx, SnapshotIterator& iter) const
-{
-    RootedObject regexp(cx, &iter.read().toObject());
-    RootedString input(cx, iter.read().toString());
-    int32_t lastIndex = iter.read().toInt32();
-
-    int32_t result;
-    if (!RegExpSearcherRaw(cx, regexp, input, lastIndex, nullptr, &result))
-        return false;
-
-    RootedValue resultVal(cx);
-    resultVal.setInt32(result);
-    iter.storeInstructionResult(resultVal);
-    return true;
-}
-
-bool
 MRegExpTester::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTester));
     return true;
 }
 
 RRegExpTester::RRegExpTester(CompactBufferReader& reader)
 { }
 
 bool
 RRegExpTester::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedString string(cx, iter.read().toString());
     RootedObject regexp(cx, &iter.read().toObject());
     int32_t lastIndex = iter.read().toInt32();
+    bool sticky = iter.read().toBoolean();
     int32_t endIndex;
 
-    if (!js::RegExpTesterRaw(cx, regexp, string, lastIndex, &endIndex))
+    if (!js::RegExpTesterRaw(cx, regexp, string, lastIndex, sticky, &endIndex))
         return false;
 
     RootedValue result(cx);
     result.setInt32(endIndex);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MRegExpReplace::writeRecoverData(CompactBufferWriter& writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpReplace));
+    return true;
+}
+
+RRegExpReplace::RRegExpReplace(CompactBufferReader& reader)
+{ }
+
+bool
+RRegExpReplace::recover(JSContext* cx, SnapshotIterator& iter) const
+{
+    RootedString string(cx, iter.read().toString());
+    Rooted<RegExpObject*> regexp(cx, &iter.read().toObject().as<RegExpObject>());
+    RootedString repl(cx, iter.read().toString());
+
+    JSString* result = js::str_replace_regexp_raw(cx, string, regexp, repl);
+    if (!result)
+        return false;
+
+    iter.storeInstructionResult(StringValue(result));
+    return true;
+}
+
+bool
 MTypeOf::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_TypeOf));
     return true;
 }
 
 RTypeOf::RTypeOf(CompactBufferReader& reader)
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -84,18 +84,18 @@ namespace jit {
     _(MinMax)                                   \
     _(Abs)                                      \
     _(Sqrt)                                     \
     _(Atan2)                                    \
     _(Hypot)                                    \
     _(MathFunction)                             \
     _(StringSplit)                              \
     _(RegExpMatcher)                            \
-    _(RegExpSearcher)                           \
     _(RegExpTester)                             \
+    _(RegExpReplace)                            \
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
     _(TruncateToInt32)                          \
     _(NewObject)                                \
     _(NewArray)                                 \
     _(NewDerivedTypedObject)                    \
@@ -566,35 +566,35 @@ class RRegExpMatcher final : public RIns
 
     virtual uint32_t numOperands() const {
         return 5;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
-class RRegExpSearcher final : public RInstruction
+class RRegExpTester final : public RInstruction
 {
   public:
-    RINSTRUCTION_HEADER_(RegExpSearcher)
+    RINSTRUCTION_HEADER_(RegExpTester)
 
     virtual uint32_t numOperands() const {
         return 5;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
-class RRegExpTester final : public RInstruction
+class RRegExpReplace final : public RInstruction
 {
   public:
-    RINSTRUCTION_HEADER_(RegExpTester)
+    RINSTRUCTION_HEADER_(RegExpReplace)
 
     virtual uint32_t numOperands() const {
-        return 5;
+        return 3;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
 class RStringReplace final : public RInstruction
 {
   private:
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -200,18 +200,18 @@ ICStub::trace(JSTracer* trc)
       case ICStub::Call_ClassHook: {
         ICCall_ClassHook* callStub = toCall_ClassHook();
         TraceNullableEdge(trc, &callStub->templateObject(), "baseline-callclasshook-template");
         break;
       }
       case ICStub::Call_StringSplit: {
         ICCall_StringSplit* callStub = toCall_StringSplit();
         TraceEdge(trc, &callStub->templateObject(), "baseline-callstringsplit-template");
-        TraceEdge(trc, &callStub->expectedSep(), "baseline-callstringsplit-sep");
-        TraceEdge(trc, &callStub->expectedStr(), "baseline-callstringsplit-str");
+        TraceEdge(trc, &callStub->expectedArg(), "baseline-callstringsplit-arg");
+        TraceEdge(trc, &callStub->expectedThis(), "baseline-callstringsplit-this");
         break;
       }
       case ICStub::GetElem_NativeSlotName:
       case ICStub::GetElem_NativeSlotSymbol:
       case ICStub::GetElem_UnboxedPropertyName: {
         ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
         getElemStub->receiverGuard().trace(trc);
         if (getElemStub->isSymbol()) {
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -1222,17 +1222,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >)         \
     _(Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2> >) \
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >)      \
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, IntPolicy<2> >)      \
     _(Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>)          \
     _(Mix3Policy<StringPolicy<0>, ObjectPolicy<1>, StringPolicy<2> >)   \
     _(Mix3Policy<StringPolicy<0>, StringPolicy<1>, StringPolicy<2> >)   \
-    _(Mix3Policy<ObjectPolicy<0>, StringPolicy<1>, IntPolicy<2>>)       \
+    _(Mix4Policy<ObjectPolicy<0>, StringPolicy<1>, IntPolicy<2>, BooleanPolicy<3>>) \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>, IntPolicy<3>>) \
     _(Mix4Policy<ObjectPolicy<0>, IntPolicy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3> >) \
     _(Mix3Policy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>)  \
     _(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
     _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >)                        \
     _(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >)   \
     _(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >)            \
     _(MixPolicy<DoublePolicy<0>, DoublePolicy<1> >)                     \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1059,16 +1059,25 @@ CreateDerivedTypedObj(JSContext* cx, Han
     MOZ_ASSERT(descr->is<TypeDescr>());
     MOZ_ASSERT(owner->is<TypedObject>());
     Rooted<TypeDescr*> descr1(cx, &descr->as<TypeDescr>());
     Rooted<TypedObject*> owner1(cx, &owner->as<TypedObject>());
     return OutlineTypedObject::createDerived(cx, descr1, owner1, offset);
 }
 
 JSString*
+RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp, HandleString repl)
+{
+    MOZ_ASSERT(string);
+    MOZ_ASSERT(repl);
+
+    return str_replace_regexp_raw(cx, string, regexp.as<RegExpObject>(), repl);
+}
+
+JSString*
 StringReplace(JSContext* cx, HandleString string, HandleString pattern, HandleString repl)
 {
     MOZ_ASSERT(string);
     MOZ_ASSERT(pattern);
     MOZ_ASSERT(repl);
 
     return str_replace_string_raw(cx, string, pattern, repl);
 }
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -688,16 +688,18 @@ bool InitBaselineFrameForOsr(BaselineFra
 
 JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
                                 HandleObject owner, int32_t offset);
 
 bool ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t deleteCount);
 
 bool Recompile(JSContext* cx);
 bool ForcedRecompile(JSContext* cx);
+JSString* RegExpReplace(JSContext* cx, HandleString string, HandleObject regexp,
+                        HandleString repl);
 JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern,
                         HandleString repl);
 
 bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index,
                                    HandleValue value, bool strict);
 
 void AssertValidObjectPtr(JSContext* cx, JSObject* obj);
 void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj);
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -160,21 +160,23 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
 
 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
 
 // Registerd used in RegExpTester instruction (do not use ReturnReg).
 static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
 
 static MOZ_CONSTEXPR_VAR FloatRegister d0  = {FloatRegisters::d0, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d1  = {FloatRegisters::d1, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d2  = {FloatRegisters::d2, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d3  = {FloatRegisters::d3, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d4  = {FloatRegisters::d4, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d5  = {FloatRegisters::d5, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d6  = {FloatRegisters::d6, VFPRegister::Double};
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -133,21 +133,23 @@ static constexpr Register AsmJSIonExitRe
 static constexpr Register AsmJSIonExitRegD0 = r0;
 static constexpr Register AsmJSIonExitRegD1 = r1;
 static constexpr Register AsmJSIonExitRegD2 = r4;
 
 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
 
 // Registerd used in RegExpTester instruction (do not use ReturnReg).
 static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
 
 static constexpr Register JSReturnReg_Type = r3;
 static constexpr Register JSReturnReg_Data = r2;
 
 static constexpr FloatRegister NANReg = { FloatRegisters::d14, FloatRegisters::Single };
 // N.B. r8 isn't listed as an aapcs temp register, but we can use it as such because we never
 // use return-structs.
 static constexpr Register CallTempNonArgRegs[] = { r8, r9, r10, r11, r12, r13, r14, r15 };
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -124,21 +124,23 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = a0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = a1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = t0;
 
 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
 
 // Registerd used in RegExpTester instruction (do not use ReturnReg).
 static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
 
 static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 4;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static MOZ_CONSTEXPR_VAR bool SupportsSimd = false;
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -4305,173 +4305,136 @@ class LRegExp : public LCallInstructionH
   public:
     LIR_HEADER(RegExp)
 
     const MRegExp* mir() const {
         return mir_->toRegExp();
     }
 };
 
-class LRegExpMatcher : public LCallInstructionHelper<BOX_PIECES, 3, 0>
+class LRegExpMatcher : public LCallInstructionHelper<BOX_PIECES, 4, 0>
 {
   public:
     LIR_HEADER(RegExpMatcher)
 
     LRegExpMatcher(const LAllocation& regexp, const LAllocation& string,
-                   const LAllocation& lastIndex)
+                   const LAllocation& lastIndex, const LAllocation& sticky)
     {
         setOperand(0, regexp);
         setOperand(1, string);
         setOperand(2, lastIndex);
+        setOperand(3, sticky);
     }
 
     const LAllocation* regexp() {
         return getOperand(0);
     }
     const LAllocation* string() {
         return getOperand(1);
     }
     const LAllocation* lastIndex() {
         return getOperand(2);
     }
+    const LAllocation* sticky() {
+        return getOperand(3);
+    }
 
     const MRegExpMatcher* mir() const {
         return mir_->toRegExpMatcher();
     }
 };
 
-class LRegExpSearcher : public LCallInstructionHelper<1, 3, 0>
-{
-  public:
-    LIR_HEADER(RegExpSearcher)
-
-    LRegExpSearcher(const LAllocation& regexp, const LAllocation& string,
-                    const LAllocation& lastIndex)
+class LRegExpTester : public LCallInstructionHelper<1, 4, 0>
+{
+  public:
+    LIR_HEADER(RegExpTester)
+    //tODO
+
+    LRegExpTester(const LAllocation& regexp, const LAllocation& string,
+                  const LAllocation& lastIndex, const LAllocation& sticky)
     {
         setOperand(0, regexp);
         setOperand(1, string);
         setOperand(2, lastIndex);
-    }
-
-    const LAllocation* regexp() {
-        return getOperand(0);
-    }
-    const LAllocation* string() {
-        return getOperand(1);
-    }
-    const LAllocation* lastIndex() {
-        return getOperand(2);
-    }
-
-    const MRegExpSearcher* mir() const {
-        return mir_->toRegExpSearcher();
-    }
-};
-
-class LRegExpTester : public LCallInstructionHelper<1, 3, 0>
-{
-  public:
-    LIR_HEADER(RegExpTester)
-
-    LRegExpTester(const LAllocation& regexp, const LAllocation& string,
-                  const LAllocation& lastIndex)
-    {
-        setOperand(0, regexp);
-        setOperand(1, string);
-        setOperand(2, lastIndex);
+        setOperand(3, sticky);
     }
 
     const LAllocation* regexp() {
         return getOperand(0);
     }
     const LAllocation* string() {
         return getOperand(1);
     }
     const LAllocation* lastIndex() {
         return getOperand(2);
     }
+    const LAllocation* sticky() {
+        return getOperand(3);
+    }
 
     const MRegExpTester* mir() const {
         return mir_->toRegExpTester();
     }
 };
 
-class LRegExpPrototypeOptimizable : public LInstructionHelper<1, 1, 1>
-{
-  public:
-    LIR_HEADER(RegExpPrototypeOptimizable);
-    explicit LRegExpPrototypeOptimizable(const LAllocation& object, const LDefinition& temp) {
-        setOperand(0, object);
-        setTemp(0, temp);
-    }
-
-    const LAllocation* object() {
-        return getOperand(0);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    MRegExpPrototypeOptimizable* mir() const {
-        return mir_->toRegExpPrototypeOptimizable();
-    }
-};
-
-class LRegExpInstanceOptimizable : public LInstructionHelper<1, 2, 1>
-{
-  public:
-    LIR_HEADER(RegExpInstanceOptimizable);
-    explicit LRegExpInstanceOptimizable(const LAllocation& object, const LAllocation& proto,
-                                        const LDefinition& temp) {
-        setOperand(0, object);
-        setOperand(1, proto);
-        setTemp(0, temp);
-    }
-
-    const LAllocation* object() {
-        return getOperand(0);
-    }
-    const LAllocation* proto() {
-        return getOperand(1);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    MRegExpInstanceOptimizable* mir() const {
-        return mir_->toRegExpInstanceOptimizable();
-    }
-};
-
-class LStringReplace: public LCallInstructionHelper<1, 3, 0>
-{
-  public:
-    LIR_HEADER(StringReplace);
-
-    LStringReplace(const LAllocation& string, const LAllocation& pattern,
+
+class LStrReplace : public LCallInstructionHelper<1, 3, 0>
+{
+  public:
+    LStrReplace(const LAllocation& string, const LAllocation& pattern,
                    const LAllocation& replacement)
     {
         setOperand(0, string);
         setOperand(1, pattern);
         setOperand(2, replacement);
     }
 
-    const MStringReplace* mir() const {
-        return mir_->toStringReplace();
-    }
-
     const LAllocation* string() {
         return getOperand(0);
     }
     const LAllocation* pattern() {
         return getOperand(1);
     }
     const LAllocation* replacement() {
         return getOperand(2);
     }
 };
 
+class LRegExpReplace: public LStrReplace
+{
+  public:
+    LIR_HEADER(RegExpReplace);
+
+    LRegExpReplace(const LAllocation& string, const LAllocation& pattern,
+                   const LAllocation& replacement)
+      : LStrReplace(string, pattern, replacement)
+    {
+    }
+
+    const MRegExpReplace* mir() const {
+        return mir_->toRegExpReplace();
+    }
+};
+
+class LStringReplace: public LStrReplace
+{
+  public:
+    LIR_HEADER(StringReplace);
+
+    LStringReplace(const LAllocation& string, const LAllocation& pattern,
+                   const LAllocation& replacement)
+      : LStrReplace(string, pattern, replacement)
+    {
+    }
+
+    const MStringReplace* mir() const {
+        return mir_->toStringReplace();
+    }
+};
+
 class LBinarySharedStub : public LCallInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(BinarySharedStub)
 
     LBinarySharedStub(const LBoxAllocation& lhs, const LBoxAllocation& rhs) {
         setBoxOperand(LhsInput, lhs);
         setBoxOperand(RhsInput, rhs);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -202,20 +202,18 @@
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
     _(RegExpMatcher)                \
-    _(RegExpSearcher)               \
     _(RegExpTester)                 \
-    _(RegExpPrototypeOptimizable)   \
-    _(RegExpInstanceOptimizable)    \
+    _(RegExpReplace)                \
     _(StringReplace)                \
     _(Substr)                       \
     _(BinarySharedStub)             \
     _(UnarySharedStub)              \
     _(NullarySharedStub)            \
     _(Lambda)                       \
     _(LambdaArrow)                  \
     _(LambdaForSingleton)           \
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -162,21 +162,23 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = rax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = rdi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = rbx;
 
 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg4;
 
 // Registerd used in RegExpTester instruction (do not use ReturnReg).
 static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg2;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg3;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg4;
 
 class ABIArgGenerator
 {
 #if defined(XP_WIN)
     unsigned regIndex_;
 #else
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -103,21 +103,23 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = edi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = eax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = esi;
 
 // Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
 static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg4;
 
 // Registerd used in RegExpTester instruction (do not use ReturnReg).
 static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg2;
 static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg3;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg4;
 
 // GCC stack is aligned on 16 bytes. Ion does not maintain this for internal
 // calls. asm.js code does.
 #if defined(__GNUC__)
 static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 16;
 #else
 static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 4;
 #endif
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -439,17 +439,16 @@ MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER,  1
 MSG_DEF(JSMSG_INVALID_OPTION_VALUE,    2, JSEXN_RANGEERR, "invalid value {1} for option {0}")
 MSG_DEF(JSMSG_INVALID_TIME_ZONE,       1, JSEXN_RANGEERR, "invalid time zone in DateTimeFormat(): {0}")
 MSG_DEF(JSMSG_UNDEFINED_CURRENCY,      0, JSEXN_TYPEERR, "undefined currency in NumberFormat() with currency style")
 
 // RegExp
 MSG_DEF(JSMSG_BACK_REF_OUT_OF_RANGE,   0, JSEXN_SYNTAXERR, "back reference out of range in regular expression")
 MSG_DEF(JSMSG_BAD_CLASS_RANGE,         0, JSEXN_SYNTAXERR, "invalid range in character class")
 MSG_DEF(JSMSG_ESCAPE_AT_END_OF_REGEXP, 0, JSEXN_SYNTAXERR, "\\ at end of pattern")
-MSG_DEF(JSMSG_EXEC_NOT_OBJORNULL,      0, JSEXN_TYPEERR, "RegExp exec method should return object or null")
 MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid decimal escape in regular expression")
 MSG_DEF(JSMSG_INVALID_GROUP,           0, JSEXN_SYNTAXERR, "invalid regexp group")
 MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression")
 MSG_DEF(JSMSG_INVALID_UNICODE_ESCAPE,  0, JSEXN_SYNTAXERR, "invalid unicode escape in regular expression")
 MSG_DEF(JSMSG_MISSING_PAREN,           0, JSEXN_SYNTAXERR, "unterminated parenthetical")
 MSG_DEF(JSMSG_NEWREGEXP_FLAGGED,       0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another")
 MSG_DEF(JSMSG_NOTHING_TO_REPEAT,       0, JSEXN_SYNTAXERR, "nothing to repeat")
 MSG_DEF(JSMSG_NUMBERS_OUT_OF_ORDER,    0, JSEXN_SYNTAXERR, "numbers out of order in {} quantifier.")
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4870,20 +4870,17 @@ GetSymbolFor(JSContext* cx, HandleString
 JS_PUBLIC_API(JSString*)
 GetSymbolDescription(HandleSymbol symbol);
 
 /* Well-known symbols. */
 #define JS_FOR_EACH_WELL_KNOWN_SYMBOL(macro) \
     macro(isConcatSpreadable) \
     macro(iterator) \
     macro(match) \
-    macro(replace) \
-    macro(search) \
     macro(species) \
-    macro(split) \
     macro(toPrimitive) \
     macro(unscopables)
 
 enum class SymbolCode : uint32_t {
     // There is one SymbolCode for each well-known symbol.
 #define JS_DEFINE_SYMBOL_ENUM(name) name,
     JS_FOR_EACH_WELL_KNOWN_SYMBOL(JS_DEFINE_SYMBOL_ENUM)  // SymbolCode::iterator, etc.
 #undef JS_DEFINE_SYMBOL_ENUM
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -626,20 +626,16 @@ struct JSCompartment
     js::DtoaCache dtoaCache;
 
     // Random number generator for Math.random().
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
 
     // Initialize randomNumberGenerator if needed.
     void ensureRandomNumberGenerator();
 
-    static size_t offsetOfRegExps() {
-        return offsetof(JSCompartment, regExps);
-    }
-
   private:
     JSCompartment* thisForCtor() { return this; }
 
   public:
     //
     // The Debugger observes execution on a frame-by-frame basis. The
     // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
     // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2275,101 +2275,80 @@ js::HasOwnProperty(JSContext* cx, Handle
     *result = (shape != nullptr);
     return true;
 }
 
 bool
 js::LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp,
                        Shape** propp)
 {
-    bool isTypedArrayOutOfRange = false;
     do {
-        if (!LookupOwnPropertyPure(cx, obj, id, propp, &isTypedArrayOutOfRange))
+        if (obj->isNative()) {
+            /* Search for a native dense element, typed array element, or property. */
+
+            if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
+                *objp = obj;
+                MarkDenseOrTypedArrayElementFound<NoGC>(propp);
+                return true;
+            }
+
+            if (obj->is<TypedArrayObject>()) {
+                uint64_t index;
+                if (IsTypedArrayIndex(id, &index)) {
+                    if (index < obj->as<TypedArrayObject>().length()) {
+                        *objp = obj;
+                        MarkDenseOrTypedArrayElementFound<NoGC>(propp);
+                    } else {
+                        *objp = nullptr;
+                        *propp = nullptr;
+                    }
+                    return true;
+                }
+            }
+
+            if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
+                *objp = obj;
+                *propp = shape;
+                return true;
+            }
+
+            // Fail if there's a resolve hook, unless the mayResolve hook tells
+            // us the resolve hook won't define a property with this id.
+            if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
+                return false;
+        } else if (obj->is<UnboxedPlainObject>()) {
+            if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
+                *objp = obj;
+                MarkNonNativePropertyFound<NoGC>(propp);
+                return true;
+            }
+        } else if (obj->is<UnboxedArrayObject>()) {
+            if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
+                *objp = obj;
+                MarkNonNativePropertyFound<NoGC>(propp);
+                return true;
+            }
+        } else if (obj->is<TypedObject>()) {
+            if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
+                *objp = obj;
+                MarkNonNativePropertyFound<NoGC>(propp);
+                return true;
+            }
+        } else {
             return false;
-
-        if (*propp) {
-            *objp = obj;
-            return true;
-        }
-
-        if (isTypedArrayOutOfRange) {
-            *objp = nullptr;
-            return true;
         }
 
         obj = obj->getProto();
     } while (obj);
 
     *objp = nullptr;
     *propp = nullptr;
     return true;
 }
 
-bool
-js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** propp,
-                          bool* isTypedArrayOutOfRange /* = nullptr */)
-{
-    JS::AutoCheckCannotGC nogc;
-    if (isTypedArrayOutOfRange)
-        *isTypedArrayOutOfRange = false;
-
-    if (obj->isNative()) {
-        // Search for a native dense element, typed array element, or property.
-
-        if (JSID_IS_INT(id) && obj->as<NativeObject>().containsDenseElement(JSID_TO_INT(id))) {
-            MarkDenseOrTypedArrayElementFound<NoGC>(propp);
-            return true;
-        }
-
-        if (obj->is<TypedArrayObject>()) {
-            uint64_t index;
-            if (IsTypedArrayIndex(id, &index)) {
-                if (index < obj->as<TypedArrayObject>().length()) {
-                    MarkDenseOrTypedArrayElementFound<NoGC>(propp);
-                } else {
-                    *propp = nullptr;
-                    if (isTypedArrayOutOfRange)
-                        *isTypedArrayOutOfRange = true;
-                }
-                return true;
-            }
-        }
-
-        if (Shape* shape = obj->as<NativeObject>().lookupPure(id)) {
-            *propp = shape;
-            return true;
-        }
-
-        // Fail if there's a resolve hook, unless the mayResolve hook tells
-        // us the resolve hook won't define a property with this id.
-        if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
-            return false;
-    } else if (obj->is<UnboxedPlainObject>()) {
-        if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
-            MarkNonNativePropertyFound<NoGC>(propp);
-            return true;
-        }
-    } else if (obj->is<UnboxedArrayObject>()) {
-        if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
-            MarkNonNativePropertyFound<NoGC>(propp);
-            return true;
-        }
-    } else if (obj->is<TypedObject>()) {
-        if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
-            MarkNonNativePropertyFound<NoGC>(propp);
-            return true;
-        }
-    } else {
-        return false;
-    }
-
-    *propp = nullptr;
-    return true;
-}
-
 static inline bool
 NativeGetPureInline(NativeObject* pobj, Shape* shape, Value* vp)
 {
     /* Fail if we have a custom getter. */
     if (!shape->hasDefaultGetter())
         return false;
 
     if (shape->hasSlot()) {
@@ -2427,52 +2406,16 @@ js::GetGetterPure(ExclusiveContext* cx, 
         return true;
     }
 
     return pobj->isNative() &&
            NativeGetGetterPureInline(&pobj->as<NativeObject>(), shape, fp);
 }
 
 bool
-js::GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native)
-{
-    JS::AutoCheckCannotGC nogc;
-    *native = nullptr;
-    Shape* shape;
-    if (!LookupOwnPropertyPure(cx, obj, id, &shape))
-        return false;
-
-    if (!shape || IsImplicitDenseOrTypedArrayElement(shape) || !shape->hasGetterObject())
-        return true;
-
-    JSObject* getterObj = shape->getterObject();
-    if (!getterObj->is<JSFunction>())
-        return true;
-
-    JSFunction* getter = &getterObj->as<JSFunction>();
-    if (!getter->isNative())
-        return true;
-
-    *native = getter->native();
-    return true;
-}
-
-bool
-js::HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result)
-{
-    Shape* shape = nullptr;
-    if (!LookupOwnPropertyPure(cx, obj, id, &shape))
-        return false;
-
-    *result = shape && !IsImplicitDenseOrTypedArrayElement(shape) && shape->hasDefaultGetter() &&
-              shape->hasSlot();
-    return true;
-}
-
-bool
 JSObject::reportReadOnly(JSContext* cx, jsid id, unsigned report)
 {
     RootedValue val(cx, IdToValue(id));
     return ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
                                  JSDVG_IGNORE_STACK, val, nullptr,
                                  nullptr, nullptr);
 }
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1213,32 +1213,22 @@ namespace js {
 extern JSObject*
 FindVariableScope(JSContext* cx, JSFunction** funp);
 
 bool
 LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp,
                    Shape** propp);
 
 bool
-LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** propp,
-                      bool* isTypedArrayOutOfRange = nullptr);
-
-bool
 GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp);
 
 bool
 GetGetterPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSFunction** fp);
 
 bool
-GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native);
-
-bool
-HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result);
-
-bool
 GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                          MutableHandle<PropertyDescriptor> desc);
 
 bool
 GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
 
 /*
  * Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1952,16 +1952,609 @@ js::str_trimLeft(JSContext* cx, unsigned
 }
 
 bool
 js::str_trimRight(JSContext* cx, unsigned argc, Value* vp)
 {
     return TrimString(cx, vp, false, true);
 }
 
+/*
+ * Perl-inspired string functions.
+ */
+
+namespace {
+
+/* Result of a successfully performed flat match. */
+class FlatMatch
+{
+    RootedAtom pat_;
+    int32_t match_;
+
+    friend class StringRegExpGuard;
+
+  public:
+    explicit FlatMatch(JSContext* cx) : pat_(cx) {}
+    JSLinearString* pattern() const { return pat_; }
+    size_t patternLength() const { return pat_->length(); }
+
+    /*
+     * Note: The match is -1 when the match is performed successfully,
+     * but no match is found.
+     */
+    int32_t match() const { return match_; }
+};
+
+} /* anonymous namespace */
+
+static inline bool
+IsRegExpMetaChar(char16_t c)
+{
+    switch (c) {
+      /* Taken from the PatternCharacter production in 15.10.1. */
+      case '^': case '$': case '\\': case '.': case '*': case '+':
+      case '?': case '(': case ')': case '[': case ']': case '{':
+      case '}': case '|':
+        return true;
+      default:
+        return false;
+    }
+}
+
+template <typename CharT>
+bool
+js::HasRegExpMetaChars(const CharT* chars, size_t length)
+{
+    for (size_t i = 0; i < length; ++i) {
+        if (IsRegExpMetaChar(chars[i]))
+            return true;
+    }
+    return false;
+}
+
+template bool
+js::HasRegExpMetaChars<Latin1Char>(const Latin1Char* chars, size_t length);
+
+template bool
+js::HasRegExpMetaChars<char16_t>(const char16_t* chars, size_t length);
+
+bool
+js::StringHasRegExpMetaChars(JSLinearString* str)
+{
+    AutoCheckCannotGC nogc;
+    if (str->hasLatin1Chars())
+        return HasRegExpMetaChars(str->latin1Chars(nogc), str->length());
+
+    return HasRegExpMetaChars(str->twoByteChars(nogc), str->length());
+}
+
+namespace {
+
+/*
+ * StringRegExpGuard factors logic out of String regexp operations.
+ *
+ * |optarg| indicates in which argument position RegExp flags will be found, if
+ * present. This is a Mozilla extension and not part of any ECMA spec.
+ */
+class MOZ_STACK_CLASS StringRegExpGuard
+{
+    RegExpGuard re_;
+    FlatMatch   fm;
+    RootedObject obj_;
+
+    /*
+     * Upper bound on the number of characters we are willing to potentially
+     * waste on searching for RegExp meta-characters.
+     */
+    static const size_t MAX_FLAT_PAT_LEN = 256;
+
+    template <typename CharT>
+    static bool
+    flattenPattern(StringBuffer& sb, const CharT* chars, size_t len)
+    {
+        static const char ESCAPE_CHAR = '\\';
+        for (const CharT* it = chars; it < chars + len; ++it) {
+            if (IsRegExpMetaChar(*it)) {
+                if (!sb.append(ESCAPE_CHAR) || !sb.append(*it))
+                    return false;
+            } else {
+                if (!sb.append(*it))
+                    return false;
+            }
+        }
+        return true;
+    }
+
+    static JSAtom*
+    flattenPattern(JSContext* cx, JSAtom* pat)
+    {
+        StringBuffer sb(cx);
+        if (!sb.reserve(pat->length()))
+            return nullptr;
+
+        if (pat->hasLatin1Chars()) {
+            AutoCheckCannotGC nogc;
+            if (!flattenPattern(sb, pat->latin1Chars(nogc), pat->length()))
+                return nullptr;
+        } else {
+            AutoCheckCannotGC nogc;
+            if (!flattenPattern(sb, pat->twoByteChars(nogc), pat->length()))
+                return nullptr;
+        }
+
+        return sb.finishAtom();
+    }
+
+  public:
+    explicit StringRegExpGuard(JSContext* cx)
+      : re_(cx), fm(cx), obj_(cx)
+    { }
+
+    /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
+    bool init(JSContext* cx, const CallArgs& args, bool convertVoid = false)
+    {
+        if (args.length() != 0) {
+            ESClassValue cls;
+            if (!GetClassOfValue(cx, args[0], &cls))
+                return false;
+
+            if (cls == ESClass_RegExp)
+                return initRegExp(cx, &args[0].toObject());
+        }
+
+        if (convertVoid && !args.hasDefined(0)) {
+            fm.pat_ = cx->runtime()->emptyString;
+            return true;
+        }
+
+        JSString* arg = ArgToRootedString(cx, args, 0);
+        if (!arg)
+            return false;
+
+        fm.pat_ = AtomizeString(cx, arg);
+        if (!fm.pat_)
+            return false;
+
+        return true;
+    }
+
+    bool initRegExp(JSContext* cx, JSObject* regexp) {
+        obj_ = regexp;
+        return RegExpToShared(cx, obj_, &re_);
+    }
+
+    bool init(JSContext* cx, HandleString pattern) {
+        fm.pat_ = AtomizeString(cx, pattern);
+        if (!fm.pat_)
+            return false;
+        return true;
+    }
+
+    /*
+     * Attempt to match |patstr| to |textstr|. A flags argument, metachars in
+     * the pattern string, or a lengthy pattern string can thwart this process.
+     *
+     * |checkMetaChars| looks for regexp metachars in the pattern string.
+     *
+     * Return whether flat matching could be used.
+     *
+     * N.B. tryFlatMatch returns nullptr on OOM, so the caller must check
+     * cx->isExceptionPending().
+     */
+    const FlatMatch*
+    tryFlatMatch(JSContext* cx, JSString* text, unsigned optarg, unsigned argc,
+                 bool checkMetaChars = true)
+    {
+        if (re_.initialized())
+            return nullptr;
+
+        if (optarg < argc)
+            return nullptr;
+
+        size_t patLen = fm.pat_->length();
+        if (checkMetaChars && (patLen > MAX_FLAT_PAT_LEN || StringHasRegExpMetaChars(fm.pat_)))
+            return nullptr;
+
+        /*
+         * |text| could be a rope, so we want to avoid flattening it for as
+         * long as possible.
+         */
+        if (text->isRope()) {
+            if (!RopeMatch(cx, &text->asRope(), fm.pat_, &fm.match_))
+                return nullptr;
+        } else {
+            fm.match_ = StringMatch(&text->asLinear(), fm.pat_, 0);
+        }
+
+        return &fm;
+    }
+
+    /* If the pattern is not already a regular expression, make it so. */
+    bool normalizeRegExp(JSContext* cx, bool flat, unsigned optarg, const CallArgs& args)
+    {
+        if (re_.initialized())
+            return true;
+
+        /* Build RegExp from pattern string. */
+        RootedString opt(cx);
+        if (optarg < args.length()) {
+            // flag argument is enabled only in release build by default.
+            // In non-release build, both telemetry and warning are still
+            // enabled, but the value of flag argument is ignored.
+
+            if (JSScript* script = cx->currentScript()) {
+                const char* filename = script->filename();
+                cx->compartment()->addTelemetry(filename, JSCompartment::DeprecatedFlagsArgument);
+            }
+
+            bool flagArgumentEnabled = cx->runtime()->options().matchFlagArgument();
+            if (!cx->compartment()->warnedAboutFlagsArgument) {
+                if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
+                                                  flagArgumentEnabled
+                                                  ? JSMSG_DEPRECATED_FLAGS_ARG
+                                                  : JSMSG_OBSOLETE_FLAGS_ARG))
+                {
+                    return false;
+                }
+                cx->compartment()->warnedAboutFlagsArgument = true;
+            }
+
+            if (flagArgumentEnabled) {
+                opt = ToString<CanGC>(cx, args[optarg]);
+                if (!opt)
+                    return false;
+            }
+        }
+
+        Rooted<JSAtom*> pat(cx);
+        if (flat) {
+            pat = flattenPattern(cx, fm.pat_);
+            if (!pat)
+                return false;
+        } else {
+            pat = fm.pat_;
+        }
+        MOZ_ASSERT(pat);
+
+        return cx->compartment()->regExps.get(cx, pat, opt, &re_);
+    }
+
+    bool zeroLastIndex(JSContext* cx) {
+        if (!regExpIsObject())
+            return true;
+
+        // Use a fast path for same-global RegExp objects with writable
+        // lastIndex.
+        if (obj_->is<RegExpObject>()) {
+            RegExpObject* nobj = &obj_->as<RegExpObject>();
+            if (nobj->lookup(cx, cx->names().lastIndex)->writable()) {
+                nobj->zeroLastIndex(cx);
+                return true;
+            }
+        }
+
+        // Handle everything else generically (including throwing if .lastIndex is non-writable).
+        RootedValue zero(cx, Int32Value(0));
+        return SetProperty(cx, obj_, cx->names().lastIndex, zero);
+    }
+
+    RegExpShared& regExp() { return *re_; }
+
+    bool regExpIsObject() { return obj_ != nullptr; }
+    HandleObject regExpObject() {
+        MOZ_ASSERT(regExpIsObject());
+        return obj_;
+    }
+
+  private:
+    StringRegExpGuard(const StringRegExpGuard&) = delete;
+    void operator=(const StringRegExpGuard&) = delete;
+};
+
+} /* anonymous namespace */
+
+static bool
+DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
+             RegExpShared& re)
+{
+    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, input, 0, sticky, &matches, nullptr);
+    if (status == RegExpRunStatus_Error)
+        return false;
+
+    if (status == RegExpRunStatus_Success_NotFound) {
+        args.rval().setNull();
+        return true;
+    }
+
+    if (!res->updateFromMatchPairs(cx, input, matches))
+        return false;
+
+    RootedValue rval(cx);
+    if (!CreateRegExpMatchResult(cx, input, matches, &rval))
+        return false;
+
+    args.rval().set(rval);
+    return true;
+}
+
+/* ES6 21.2.5.2.3. */
+static size_t
+AdvanceStringIndex(HandleLinearString input, size_t length, size_t index, bool unicode)
+{
+    /* Steps 1-3 (implicit). */
+
+    /* Step 4: If input is latin1, there is no surrogate pair. */
+    if (!unicode || input->hasLatin1Chars())
+        return index + 1;
+
+    JS::AutoCheckCannotGC nogc;
+    const char16_t* S = input->twoByteChars(nogc);
+
+    /* Step 6. */
+    if (index + 1 >= length)
+        return index + 1;
+
+    /* Step 7. */
+    char16_t first = S[index];
+
+    /* Step 8. */
+    if (!unicode::IsLeadSurrogate(first))
+        return index + 1;
+
+    /* Step 9. */
+    char16_t second = S[index + 1];
+
+    /* Step 10. */
+    if (!unicode::IsTrailSurrogate(second))
+        return index + 1;
+
+    /* Step 11. */
+    return index + 2;
+}
+
+/* ES5 15.5.4.10 step 8. */
+static bool
+DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
+              StringRegExpGuard& g)
+{
+    // Step 8a.
+    //
+    // This single zeroing of "lastIndex" covers all "lastIndex" changes in the
+    // rest of String.prototype.match, particularly in steps 8f(i) and
+    // 8f(iii)(2)(a).  Here's why.
+    //
+    // The inputs to the calls to RegExp.prototype.exec are a RegExp object
+    // whose .global is true and a string.  The only side effect of a call in
+    // these circumstances is that the RegExp's .lastIndex will be modified to
+    // the next starting index after the discovered match (or to 0 if there's
+    // no remaining match).  Because .lastIndex is a non-configurable data
+    // property and no script-controllable code executes after step 8a, passing
+    // step 8a implies *every* .lastIndex set succeeds.  String.prototype.match
+    // calls RegExp.prototype.exec repeatedly, and the last call doesn't match,
+    // so the final value of .lastIndex is 0: exactly the state after step 8a
+    // succeeds.  No spec step lets script observe intermediate .lastIndex
+    // values.
+    //
+    // The arrays returned by RegExp.prototype.exec always have a string at
+    // index 0, for which [[Get]]s have no side effects.
+    //
+    // Filling in a new array using [[DefineOwnProperty]] is unobservable.
+    //
+    // This is a tricky point, because after this set, our implementation *can*
+    // fail.  The key is that script can't distinguish these failure modes from
+    // one where, in spec terms, we fail immediately after step 8a.  That *in
+    // reality* we might have done extra matching work, or created a partial
+    // results array to return, or hit an interrupt, is irrelevant.  The
+    // script can't tell we did any of those things but didn't update
+    // .lastIndex.  Thus we can optimize steps 8b onward however we want,
+    // including eliminating intermediate .lastIndex sets, as long as we don't
+    // add ways for script to observe the intermediate states.
+    //
+    // In short: it's okay to cheat (by setting .lastIndex to 0, once) because
+    // we can't get caught.
+    if (!g.zeroLastIndex(cx))
+        return false;
+
+    // Step 8b.
+    AutoValueVector elements(cx);
+
+    size_t lastSuccessfulStart = 0;
+
+    // The loop variables from steps 8c-e aren't needed, as we use different
+    // techniques from the spec to implement step 8f's loop.
+
+    // Step 8f.
+    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    size_t charsLen = input->length();
+    RegExpShared& re = g.regExp();
+    bool unicode = re.unicode();
+    bool sticky = re.sticky();
+    for (size_t searchIndex = 0; searchIndex <= charsLen; ) {
+        if (!CheckForInterrupt(cx))
+            return false;
+
+        // Steps 8f(i-ii), minus "lastIndex" updates (see above).
+        RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, &matches, nullptr);
+        if (status == RegExpRunStatus_Error)
+            return false;
+
+        // Step 8f(ii).
+        if (status == RegExpRunStatus_Success_NotFound)
+            break;
+
+        lastSuccessfulStart = searchIndex;
+        MatchPair& match = matches[0];
+
+        // Steps 8f(iii)(1-3).
+        searchIndex = match.isEmpty()
+                      ? AdvanceStringIndex(input, charsLen, match.limit, unicode)
+                      : match.limit;
+
+        // Step 8f(iii)(4-5).
+        JSLinearString* str = NewDependentString(cx, input, match.start, match.length());
+        if (!str)
+            return false;
+        if (!elements.append(StringValue(str)))
+            return false;
+    }
+
+    // Step 8g.
+    if (elements.empty()) {
+        args.rval().setNull();
+        return true;
+    }
+
+    // The last *successful* match updates the RegExpStatics. (Interestingly,
+    // this implies that String.prototype.match's semantics aren't those
+    // implied by the RegExp.prototype.exec calls in the ES5 algorithm.)
+    res->updateLazily(cx, input, &re, lastSuccessfulStart, sticky);
+
+    // Steps 8b, 8f(iii)(5-6), 8h.
+    JSObject* array = NewDenseCopiedArray(cx, elements.length(), elements.begin());
+    if (!array)
+        return false;
+
+    args.rval().setObject(*array);
+    return true;
+}
+
+static bool
+BuildFlatMatchArray(JSContext* cx, HandleString textstr, const FlatMatch& fm, CallArgs* args)
+{
+    if (fm.match() < 0) {
+        args->rval().setNull();
+        return true;
+    }
+
+    /* Get the templateObject that defines the shape and type of the output object */
+    JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
+    if (!templateObject)
+        return false;
+
+    RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, 1, templateObject));
+    if (!arr)
+        return false;
+
+    /* Store a Value for each pair. */
+    arr->setDenseInitializedLength(1);
+    arr->initDenseElement(0, StringValue(fm.pattern()));
+
+    /* Set the |index| property. (TemplateObject positions it in slot 0) */
+    arr->setSlot(0, Int32Value(fm.match()));
+
+    /* Set the |input| property. (TemplateObject positions it in slot 1) */
+    arr->setSlot(1, StringValue(textstr));
+
+#ifdef DEBUG
+    RootedValue test(cx);
+    RootedId id(cx, NameToId(cx->names().index));
+    if (!NativeGetProperty(cx, arr, id, &test))
+        return false;
+    MOZ_ASSERT(test == arr->getSlot(0));
+    id = NameToId(cx->names().input);
+    if (!NativeGetProperty(cx, arr, id, &test))
+        return false;
+    MOZ_ASSERT(test == arr->getSlot(1));
+#endif
+
+    args->rval().setObject(*arr);
+    return true;
+}
+
+/* ES5 15.5.4.10. */
+bool
+js::str_match(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Steps 1-2. */
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
+    if (!str)
+        return false;
+
+    /* Steps 3-4, plus the trailing-argument "flags" extension. */
+    StringRegExpGuard g(cx);
+    if (!g.init(cx, args, true))
+        return false;
+
+    /* Fast path when the search pattern can be searched for as a string. */
+    if (const FlatMatch* fm = g.tryFlatMatch(cx, str, 1, args.length()))
+        return BuildFlatMatchArray(cx, str, *fm, &args);
+
+    /* Return if there was an error in tryFlatMatch. */
+    if (cx->isExceptionPending())
+        return false;
+
+    /* Create regular-expression internals as needed to perform the match. */
+    if (!g.normalizeRegExp(cx, false, 1, args))
+        return false;
+
+    RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
+
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
+        return false;
+
+    /* Steps 5-6, 7. */
+    if (!g.regExp().global())
+        return DoMatchLocal(cx, args, res, linearStr, g.regExp());
+
+    /* Steps 6, 8. */
+    return DoMatchGlobal(cx, args, res, linearStr, g);
+}
+
+bool
+js::str_search(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
+    if (!str)
+        return false;
+
+    StringRegExpGuard g(cx);
+    if (!g.init(cx, args, true))
+        return false;
+    if (const FlatMatch* fm = g.tryFlatMatch(cx, str, 1, args.length())) {
+        args.rval().setInt32(fm->match());
+        return true;
+    }
+
+    if (cx->isExceptionPending())  /* from tryFlatMatch */
+        return false;
+
+    if (!g.normalizeRegExp(cx, false, 1, args))
+        return false;
+
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
+        return false;
+
+    RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return false;
+
+    /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
+    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    RegExpShared& re = g.regExp();
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, linearStr, 0, sticky, &matches, nullptr);
+    if (status == RegExpRunStatus_Error)
+        return false;
+
+    if (status == RegExpRunStatus_Success)
+        res->updateLazily(cx, linearStr, &re, 0, sticky);
+
+    args.rval().setInt32(status == RegExpRunStatus_Success_NotFound ? -1 : matches[0].start);
+    return true;
+}
+
 // Utility for building a rope (lazy concatenation) of strings.
 class RopeBuilder {
     JSContext* cx;
     RootedString res;
 
     RopeBuilder(const RopeBuilder& other) = delete;
     void operator=(const RopeBuilder& other) = delete;
 
@@ -1989,24 +2582,404 @@ FindDollarIndex(const CharT* chars, size
     if (const CharT* p = js_strchr_limit(chars, '$', chars + length)) {
         uint32_t dollarIndex = p - chars;
         MOZ_ASSERT(dollarIndex < length);
         return dollarIndex;
     }
     return UINT32_MAX;
 }
 
+struct ReplaceData
+{
+    explicit ReplaceData(JSContext* cx)
+      : str(cx), g(cx), lambda(cx), elembase(cx), repstr(cx),
+        fig(cx, NullValue()), sb(cx)
+    {}
+
+    inline void setReplacementString(JSLinearString* string) {
+        MOZ_ASSERT(string);
+        lambda = nullptr;
+        elembase = nullptr;
+        repstr = string;
+
+        AutoCheckCannotGC nogc;
+        dollarIndex = string->hasLatin1Chars()
+                      ? FindDollarIndex(string->latin1Chars(nogc), string->length())
+                      : FindDollarIndex(string->twoByteChars(nogc), string->length());
+    }
+
+    inline void setReplacementFunction(JSObject* func) {
+        MOZ_ASSERT(func);
+        lambda = func;
+        elembase = nullptr;
+        repstr = nullptr;
+        dollarIndex = UINT32_MAX;
+    }
+
+    RootedString       str;            /* 'this' parameter object as a string */
+    StringRegExpGuard  g;              /* regexp parameter object and private data */
+    RootedObject       lambda;         /* replacement function object or null */
+    RootedNativeObject elembase;       /* object for function(a){return b[a]} replace */
+    RootedLinearString repstr;         /* replacement string */
+    uint32_t           dollarIndex;    /* index of first $ in repstr, or UINT32_MAX */
+    int                leftIndex;      /* left context index in str->chars */
+    bool               calledBack;     /* record whether callback has been called */
+    FastCallGuard      fig;            /* used for lambda calls, also holds arguments */
+    StringBuffer       sb;             /* buffer built during DoMatch */
+};
+
 } /* anonymous namespace */
 
+static bool
+ReplaceRegExp(JSContext* cx, RegExpStatics* res, ReplaceData& rdata);
+
+static bool
+DoMatchForReplaceLocal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
+                       RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
+{
+    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, linearStr, 0, sticky, &matches, nullptr);
+    if (status == RegExpRunStatus_Error)
+        return false;
+
+    if (status == RegExpRunStatus_Success_NotFound)
+        return true;
+
+    MatchPair& match = matches[0];
+    *rightContextOffset = match.limit;
+
+    if (!res->updateFromMatchPairs(cx, linearStr, matches))
+        return false;
+
+    return ReplaceRegExp(cx, res, rdata);
+}
+
+static bool
+DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
+                        RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
+{
+    bool unicode = re.unicode();
+    bool sticky = re.sticky();
+    size_t charsLen = linearStr->length();
+    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    for (size_t count = 0, searchIndex = 0; searchIndex <= charsLen; ++count) {
+        if (!CheckForInterrupt(cx))
+            return false;
+
+        RegExpRunStatus status = re.execute(cx, linearStr, searchIndex, sticky, &matches, nullptr);
+        if (status == RegExpRunStatus_Error)
+            return false;
+
+        if (status == RegExpRunStatus_Success_NotFound)
+            break;
+
+        MatchPair& match = matches[0];
+        searchIndex = match.isEmpty()
+                      ? AdvanceStringIndex(linearStr, charsLen, match.limit, unicode)
+                      : match.limit;
+        *rightContextOffset = match.limit;
+
+        if (!res->updateFromMatchPairs(cx, linearStr, matches))
+            return false;
+
+        if (!ReplaceRegExp(cx, res, rdata))
+            return false;
+    }
+
+    return true;
+}
+
+template <typename CharT>
+static bool
+InterpretDollar(RegExpStatics* res, const CharT* bp, const CharT* dp, const CharT* ep,
+                ReplaceData& rdata, JSSubString* out, size_t* skip)
+{
+    MOZ_ASSERT(*dp == '$');
+
+    /* If there is only a dollar, bail now */
+    if (dp + 1 >= ep)
+        return false;
+
+    /* Interpret all Perl match-induced dollar variables. */
+    char16_t dc = dp[1];
+    if (JS7_ISDEC(dc)) {
+        /* ECMA-262 Edition 3: 1-9 or 01-99 */
+        unsigned num = JS7_UNDEC(dc);
+        if (num > res->getMatches().parenCount())
+            return false;
+
+        const CharT* cp = dp + 2;
+        if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
+            unsigned tmp = 10 * num + JS7_UNDEC(dc);
+            if (tmp <= res->getMatches().parenCount()) {
+                cp++;
+                num = tmp;
+            }
+        }
+        if (num == 0)
+            return false;
+
+        *skip = cp - dp;
+
+        MOZ_ASSERT(num <= res->getMatches().parenCount());
+
+        /*
+         * Note: we index to get the paren with the (1-indexed) pair
+         * number, as opposed to a (0-indexed) paren number.
+         */
+        res->getParen(num, out);
+        return true;
+    }
+
+    *skip = 2;
+    switch (dc) {
+      case '$':
+        out->init(rdata.repstr, dp - bp, 1);
+        return true;
+      case '&':
+        res->getLastMatch(out);
+        return true;
+      case '+':
+        res->getLastParen(out);
+        return true;
+      case '`':
+        res->getLeftContext(out);
+        return true;
+      case '\'':
+        res->getRightContext(out);
+        return true;
+    }
+    return false;
+}
+
+template <typename CharT>
+static bool
+FindReplaceLengthString(JSContext* cx, RegExpStatics* res, ReplaceData& rdata, size_t* sizep)
+{
+    JSLinearString* repstr = rdata.repstr;
+    CheckedInt<uint32_t> replen = repstr->length();
+
+    if (rdata.dollarIndex != UINT32_MAX) {
+        AutoCheckCannotGC nogc;
+        MOZ_ASSERT(rdata.dollarIndex < repstr->length());
+        const CharT* bp = repstr->chars<CharT>(nogc);
+        const CharT* dp = bp + rdata.dollarIndex;
+        const CharT* ep = bp + repstr->length();
+        do {
+            JSSubString sub;
+            size_t skip;
+            if (InterpretDollar(res, bp, dp, ep, rdata, &sub, &skip)) {
+                if (sub.length > skip)
+                    replen += sub.length - skip;
+                else
+                    replen -= skip - sub.length;
+                dp += skip;
+            } else {
+                dp++;
+            }
+
+            dp = js_strchr_limit(dp, '$', ep);
+        } while (dp);
+    }
+
+    if (!replen.isValid()) {
+        ReportAllocationOverflow(cx);
+        return false;
+    }
+
+    *sizep = replen.value();
+    return true;
+}
+
+static bool
+FindReplaceLength(JSContext* cx, RegExpStatics* res, ReplaceData& rdata, size_t* sizep)
+{
+    if (rdata.elembase) {
+        /*
+         * The base object is used when replace was passed a lambda which looks like
+         * 'function(a) { return b[a]; }' for the base object b.  b will not change
+         * in the course of the replace unless we end up making a scripted call due
+         * to accessing a scripted getter or a value with a scripted toString.
+         */
+        MOZ_ASSERT(rdata.lambda);
+        MOZ_ASSERT(!rdata.elembase->getOpsLookupProperty());
+        MOZ_ASSERT(!rdata.elembase->getOpsGetProperty());
+
+        RootedValue match(cx);
+        if (!res->createLastMatch(cx, &match))
+            return false;
+        JSAtom* atom = ToAtom<CanGC>(cx, match);
+        if (!atom)
+            return false;
+
+        RootedValue v(cx);
+        if (HasDataProperty(cx, rdata.elembase, AtomToId(atom), v.address()) && v.isString()) {
+            rdata.repstr = v.toString()->ensureLinear(cx);
+            if (!rdata.repstr)
+                return false;
+            *sizep = rdata.repstr->length();
+            return true;
+        }
+
+        /*
+         * Couldn't handle this property, fall through and despecialize to the
+         * general lambda case.
+         */
+        rdata.elembase = nullptr;
+    }
+
+    if (rdata.lambda) {
+        RootedValue lambda(cx, ObjectValue(*rdata.lambda));
+        RootedValue thisv(cx, UndefinedValue());
+        RootedValue rval(cx);
+
+        /*
+         * In the lambda case, not only do we find the replacement string's
+         * length, we compute repstr and return it via rdata for use within
+         * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
+         * index, input), i.e., all the properties of a regexp match array.
+         * For $&, etc., we must create string jsvals from cx->regExpStatics.
+         * We grab up stack space to keep the newborn strings GC-rooted.
+         */
+        unsigned p = res->getMatches().parenCount();
+        unsigned argc = 1 + p + 2;
+
+        InvokeArgs& args = rdata.fig.args();
+        if (!args.init(argc))
+            return false;
+
+        /* Push $&, $1, $2, ... */
+        unsigned argi = 0;
+        if (!res->createLastMatch(cx, args[argi++]))
+            return false;
+
+        for (size_t i = 0; i < res->getMatches().parenCount(); ++i) {
+            if (!res->createParen(cx, i + 1, args[argi++]))
+                return false;
+        }
+
+        /* Push match index and input string. */
+        args[argi++].setInt32(res->getMatches()[0].start);
+        args[argi].setString(rdata.str);
+
+        if (!rdata.fig.call(cx, lambda, thisv, &rval))
+            return false;
+
+        /* root repstr: rdata is on the stack, so scanned by conservative gc. */
+        JSString* repstr = ToString<CanGC>(cx, rval);
+        if (!repstr)
+            return false;
+        rdata.repstr = repstr->ensureLinear(cx);
+        if (!rdata.repstr)
+            return false;
+        *sizep = rdata.repstr->length();
+        return true;
+    }
+
+    return rdata.repstr->hasLatin1Chars()
+           ? FindReplaceLengthString<Latin1Char>(cx, res, rdata, sizep)
+           : FindReplaceLengthString<char16_t>(cx, res, rdata, sizep);
+}
+
+/*
+ * Precondition: |rdata.sb| already has necessary growth space reserved (as
+ * derived from FindReplaceLength), and has been inflated to TwoByte if
+ * necessary.
+ */
+template <typename CharT>
+static void
+DoReplace(RegExpStatics* res, ReplaceData& rdata)
+{
+    AutoCheckCannotGC nogc;
+    JSLinearString* repstr = rdata.repstr;
+    const CharT* bp = repstr->chars<CharT>(nogc);
+    const CharT* cp = bp;
+
+    if (rdata.dollarIndex != UINT32_MAX) {
+        MOZ_ASSERT(rdata.dollarIndex < repstr->length());
+        const CharT* dp = bp + rdata.dollarIndex;
+        const CharT* ep = bp + repstr->length();
+        do {
+            /* Move one of the constant portions of the replacement value. */
+            size_t len = dp - cp;
+            rdata.sb.infallibleAppend(cp, len);
+            cp = dp;
+
+            JSSubString sub;
+            size_t skip;
+            if (InterpretDollar(res, bp, dp, ep, rdata, &sub, &skip)) {
+                rdata.sb.infallibleAppendSubstring(sub.base, sub.offset, sub.length);
+                cp += skip;
+                dp += skip;
+            } else {
+                dp++;
+            }
+
+            dp = js_strchr_limit(dp, '$', ep);
+        } while (dp);
+    }
+    rdata.sb.infallibleAppend(cp, repstr->length() - (cp - bp));
+}
+
+static bool
+ReplaceRegExp(JSContext* cx, RegExpStatics* res, ReplaceData& rdata)
+{
+
+    const MatchPair& match = res->getMatches()[0];
+    MOZ_ASSERT(!match.isUndefined());
+    MOZ_ASSERT(match.limit >= match.start && match.limit >= 0);
+
+    rdata.calledBack = true;
+    size_t leftoff = rdata.leftIndex;
+    size_t leftlen = match.start - leftoff;
+    rdata.leftIndex = match.limit;
+
+    size_t replen = 0;  /* silence 'unused' warning */
+    if (!FindReplaceLength(cx, res, rdata, &replen))
+        return false;
+
+    CheckedInt<uint32_t> newlen(rdata.sb.length());
+    newlen += leftlen;
+    newlen += replen;
+    if (!newlen.isValid()) {
+        ReportAllocationOverflow(cx);
+        return false;
+    }
+
+    /*
+     * Inflate the buffer now if needed, to avoid (fallible) Latin1 to TwoByte
+     * inflation later on.
+     */
+    JSLinearString& str = rdata.str->asLinear();  /* flattened for regexp */
+    if (str.hasTwoByteChars() || rdata.repstr->hasTwoByteChars()) {
+        if (!rdata.sb.ensureTwoByteChars())
+            return false;
+    }
+
+    if (!rdata.sb.reserve(newlen.value()))
+        return false;
+
+    /* Append skipped-over portion of the search value. */
+    rdata.sb.infallibleAppendSubstring(&str, leftoff, leftlen);
+
+    if (rdata.repstr->hasLatin1Chars())
+        DoReplace<Latin1Char>(res, rdata);
+    else
+        DoReplace<char16_t>(res, rdata);
+    return true;
+}
+
 static JSString*
 BuildFlatReplacement(JSContext* cx, HandleString textstr, HandleString repstr,
-                     size_t match, size_t patternLength)
+                     const FlatMatch& fm)
 {
     RopeBuilder builder(cx);
-    size_t matchEnd = match + patternLength;
+    size_t match = fm.match();
+    size_t matchEnd = match + fm.patternLength();
 
     if (textstr->isRope()) {
         /*
          * If we are replacing over a rope, avoid flattening it by iterating
          * through it, building a new rope.
          */
         StringSegmentRange r(cx);
         if (!r.init(textstr))
@@ -2056,38 +3029,41 @@ BuildFlatReplacement(JSContext* cx, Hand
             if (!r.popFront())
                 return nullptr;
         }
     } else {
         RootedString leftSide(cx, NewDependentString(cx, textstr, 0, match));
         if (!leftSide)
             return nullptr;
         RootedString rightSide(cx);
-        rightSide = NewDependentString(cx, textstr, match + patternLength,
-                                       textstr->length() - match - patternLength);
+        rightSide = NewDependentString(cx, textstr, match + fm.patternLength(),
+                                       textstr->length() - match - fm.patternLength());
         if (!rightSide ||
             !builder.append(leftSide) ||
             !builder.append(repstr) ||
             !builder.append(rightSide))
         {
             return nullptr;
         }
     }
 
     return builder.result();
 }
 
 template <typename CharT>
 static bool
 AppendDollarReplacement(StringBuffer& newReplaceChars, size_t firstDollarIndex,
-                        size_t matchStart, size_t matchLimit, JSLinearString* text,
+                        const FlatMatch& fm, JSLinearString* text,
                         const CharT* repChars, size_t repLength)
 {
     MOZ_ASSERT(firstDollarIndex < repLength);
 
+    size_t matchStart = fm.match();
+    size_t matchLimit = matchStart + fm.patternLength();
+
     /* Move the pre-dollar chunk in bulk. */
     newReplaceChars.infallibleAppend(repChars, firstDollarIndex);
 
     /* Move the rest char-by-char, interpreting dollars as we encounter them. */
     const CharT* repLimit = repChars + repLength;
     for (const CharT* it = repChars + firstDollarIndex; it < repLimit; ++it) {
         if (*it != '$' || it == repLimit - 1) {
             if (!newReplaceChars.append(*it))
@@ -2126,47 +3102,48 @@ AppendDollarReplacement(StringBuffer& ne
 /*
  * Perform a linear-scan dollar substitution on the replacement text,
  * constructing a result string that looks like:
  *
  *      newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:]
  */
 static JSString*
 BuildDollarReplacement(JSContext* cx, JSString* textstrArg, JSLinearString* repstr,
-                       uint32_t firstDollarIndex, size_t matchStart, size_t patternLength)
+                       uint32_t firstDollarIndex, const FlatMatch& fm)
 {
     RootedLinearString textstr(cx, textstrArg->ensureLinear(cx));
     if (!textstr)
         return nullptr;
 
-    size_t matchLimit = matchStart + patternLength;
+    size_t matchStart = fm.match();
+    size_t matchLimit = matchStart + fm.patternLength();
 
     /*
      * Most probably:
      *
      *      len(newstr) >= len(orig) - len(match) + len(replacement)
      *
      * Note that dollar vars _could_ make the resulting text smaller than this.
      */
     StringBuffer newReplaceChars(cx);
     if (repstr->hasTwoByteChars() && !newReplaceChars.ensureTwoByteChars())
         return nullptr;
 
-    if (!newReplaceChars.reserve(textstr->length() - patternLength + repstr->length()))
+    if (!newReplaceChars.reserve(textstr->length() - fm.patternLength() + repstr->length()))
         return nullptr;
 
     bool res;
     if (repstr->hasLatin1Chars()) {
         AutoCheckCannotGC nogc;
-        res = AppendDollarReplacement(newReplaceChars, firstDollarIndex, matchStart, matchLimit,
-                                      textstr, repstr->latin1Chars(nogc), repstr->length());
+        res = AppendDollarReplacement(newReplaceChars, firstDollarIndex, fm, textstr,
+                                      repstr->latin1Chars(nogc), repstr->length());
     } else {
         AutoCheckCannotGC nogc;
-        res = AppendDollarReplacement(newReplaceChars, firstDollarIndex, matchStart, matchLimit,
-                                      textstr, repstr->twoByteChars(nogc), repstr->length());
+        res = AppendDollarReplacement(newReplaceChars, firstDollarIndex, fm, textstr,
+                                      repstr->twoByteChars(nogc), repstr->length());
     }
     if (!res)
         return nullptr;
 
     RootedString leftSide(cx, NewDependentString(cx, textstr, 0, matchStart));
     if (!leftSide)
         return nullptr;
 
@@ -2182,16 +3159,299 @@ BuildDollarReplacement(JSContext* cx, JS
 
     RopeBuilder builder(cx);
     if (!builder.append(leftSide) || !builder.append(newReplace) || !builder.append(rightSide))
         return nullptr;
 
     return builder.result();
 }
 
+struct StringRange
+{
+    size_t start;
+    size_t length;
+
+    StringRange(size_t s, size_t l)
+      : start(s), length(l)
+    { }
+};
+
+template <typename CharT>
+static void
+CopySubstringsToFatInline(JSFatInlineString* dest, const CharT* src, const StringRange* ranges,
+                          size_t rangesLen, size_t outputLen)
+{
+    CharT* buf = dest->init<CharT>(outputLen);
+    size_t pos = 0;
+    for (size_t i = 0; i < rangesLen; i++) {
+        PodCopy(buf + pos, src + ranges[i].start, ranges[i].length);
+        pos += ranges[i].length;
+    }
+
+    MOZ_ASSERT(pos == outputLen);
+    buf[outputLen] = 0;
+}
+
+static inline JSFatInlineString*
+FlattenSubstrings(JSContext* cx, HandleLinearString str, const StringRange* ranges,
+                  size_t rangesLen, size_t outputLen)
+{
+    JSFatInlineString* result = Allocate<JSFatInlineString>(cx);
+    if (!result)
+        return nullptr;
+
+    AutoCheckCannotGC nogc;
+    if (str->hasLatin1Chars())
+        CopySubstringsToFatInline(result, str->latin1Chars(nogc), ranges, rangesLen, outputLen);
+    else
+        CopySubstringsToFatInline(result, str->twoByteChars(nogc), ranges, rangesLen, outputLen);
+    return result;
+}
+
+static JSString*
+AppendSubstrings(JSContext* cx, HandleLinearString str, const StringRange* ranges,
+                 size_t rangesLen)
+{
+    MOZ_ASSERT(rangesLen);
+
+    /* For single substrings, construct a dependent string. */
+    if (rangesLen == 1)
+        return NewDependentString(cx, str, ranges[0].start, ranges[0].length);
+
+    bool isLatin1 = str->hasLatin1Chars();
+    uint32_t fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
+    if (isLatin1)
+        fatInlineMaxLength = JSFatInlineString::MAX_LENGTH_LATIN1;
+
+    /* Collect substrings into a rope */
+    size_t i = 0;
+    RopeBuilder rope(cx);
+    RootedString part(cx, nullptr);
+    while (i < rangesLen) {
+
+        /* Find maximum range that fits in JSFatInlineString */
+        size_t substrLen = 0;
+        size_t end = i;
+        for (; end < rangesLen; end++) {
+            if (substrLen + ranges[end].length > fatInlineMaxLength)
+                break;
+            substrLen += ranges[end].length;
+        }
+
+        if (i == end) {
+            /* Not even one range fits JSFatInlineString, use DependentString */
+            const StringRange& sr = ranges[i++];
+            part = NewDependentString(cx, str, sr.start, sr.length);
+        } else {
+            /* Copy the ranges (linearly) into a JSFatInlineString */
+            part = FlattenSubstrings(cx, str, ranges + i, end - i, substrLen);
+            i = end;
+        }
+
+        if (!part)
+            return nullptr;
+
+        /* Appending to the rope permanently roots the substring. */
+        if (!rope.append(part))
+            return nullptr;
+    }
+
+    return rope.result();
+}
+
+static JSString*
+StrReplaceRegexpRemove(JSContext* cx, HandleString str, RegExpShared& re)
+{
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
+        return nullptr;
+
+    Vector<StringRange, 16, SystemAllocPolicy> ranges;
+
+    size_t charsLen = linearStr->length();
+
+    ScopedMatchPairs matches(&cx->tempLifoAlloc());
+    size_t startIndex = 0; /* Index used for iterating through the string. */
+    size_t lastIndex = 0;  /* Index after last successful match. */
+    size_t lazyIndex = 0;  /* Index before last successful match. */
+
+    /* Accumulate StringRanges for unmatched substrings. */
+    bool unicode = re.unicode();
+    bool sticky = re.sticky();
+    while (startIndex <= charsLen) {
+        if (!CheckForInterrupt(cx))
+            return nullptr;
+
+        RegExpRunStatus status = re.execute(cx, linearStr, startIndex, sticky, &matches, nullptr);
+        if (status == RegExpRunStatus_Error)
+            return nullptr;
+        if (status == RegExpRunStatus_Success_NotFound)
+            break;
+        MatchPair& match = matches[0];
+
+        /* Include the latest unmatched substring. */
+        if (size_t(match.start) > lastIndex) {
+            if (!ranges.append(StringRange(lastIndex, match.start - lastIndex)))
+                return nullptr;
+        }
+
+        lazyIndex = lastIndex;
+        lastIndex = match.limit;
+
+        startIndex = match.isEmpty()
+                     ? AdvanceStringIndex(linearStr, charsLen, match.limit, unicode)
+                     : match.limit;
+
+        /* Non-global removal executes at most once. */
+        if (!re.global())
+            break;
+    }
+
+    RegExpStatics* res;
+
+    /* If unmatched, return the input string. */
+    if (!lastIndex) {
+        if (startIndex > 0) {
+            res = cx->global()->getRegExpStatics(cx);
+            if (!res)
+                return nullptr;
+            res->updateLazily(cx, linearStr, &re, lazyIndex, sticky);
+        }
+
+        return str;
+    }
+
+    /* The last successful match updates the RegExpStatics. */
+    res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return nullptr;
+
+    res->updateLazily(cx, linearStr, &re, lazyIndex, sticky);
+
+    /* Include any remaining part of the string. */
+    if (lastIndex < charsLen) {
+        if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
+            return nullptr;
+    }
+
+    /* Handle the empty string before calling .begin(). */
+    if (ranges.empty())
+        return cx->runtime()->emptyString;
+
+    return AppendSubstrings(cx, linearStr, ranges.begin(), ranges.length());
+}
+
+static inline JSString*
+StrReplaceRegExp(JSContext* cx, ReplaceData& rdata)
+{
+    rdata.leftIndex = 0;
+    rdata.calledBack = false;
+
+    RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+    if (!res)
+        return nullptr;
+
+    RegExpShared& re = rdata.g.regExp();
+
+    // The spec doesn't describe this function very clearly, so we go ahead and
+    // assume that when the input to String.prototype.replace is a global
+    // RegExp, calling the replacer function (assuming one was provided) takes
+    // place only after the matching is done. See the comment at the beginning
+    // of DoMatchGlobal explaining why we can zero the the RegExp object's
+    // lastIndex property here.
+    if (re.global() && !rdata.g.zeroLastIndex(cx))
+        return nullptr;
+
+    /* Optimize removal. */
+    if (rdata.repstr && rdata.repstr->length() == 0) {
+        MOZ_ASSERT(!rdata.lambda && !rdata.elembase && rdata.dollarIndex == UINT32_MAX);
+        return StrReplaceRegexpRemove(cx, rdata.str, re);
+    }
+
+    RootedLinearString linearStr(cx, rdata.str->ensureLinear(cx));
+    if (!linearStr)
+        return nullptr;
+
+    size_t rightContextOffset = 0;
+    if (re.global()) {
+        if (!DoMatchForReplaceGlobal(cx, res, linearStr, re, rdata, &rightContextOffset))
+            return nullptr;
+    } else {
+        if (!DoMatchForReplaceLocal(cx, res, linearStr, re, rdata, &rightContextOffset))
+            return nullptr;
+    }
+
+    if (!rdata.calledBack) {
+        /* Didn't match, so the string is unmodified. */
+        return rdata.str;
+    }
+
+    MOZ_ASSERT(rightContextOffset <= rdata.str->length());
+    size_t length = rdata.str->length() - rightContextOffset;
+    if (!rdata.sb.appendSubstring(rdata.str, rightContextOffset, length))
+        return nullptr;
+
+    return rdata.sb.finishString();
+}
+
+static inline bool
+str_replace_regexp(JSContext* cx, const CallArgs& args, ReplaceData& rdata)
+{
+    if (!rdata.g.normalizeRegExp(cx, true, 2, args))
+        return false;
+
+    JSString* res = StrReplaceRegExp(cx, rdata);
+    if (!res)
+        return false;
+
+    args.rval().setString(res);
+    return true;
+}
+
+JSString*
+js::str_replace_regexp_raw(JSContext* cx, HandleString string, Handle<RegExpObject*> regexp,
+                           HandleString replacement)
+{
+    /* Optimize removal, so we don't have to create ReplaceData */
+    if (replacement->length() == 0) {
+        StringRegExpGuard guard(cx);
+        if (!guard.initRegExp(cx, regexp))
+            return nullptr;
+
+        RegExpShared& re = guard.regExp();
+        return StrReplaceRegexpRemove(cx, string, re);
+    }
+
+    ReplaceData rdata(cx);
+    rdata.str = string;
+
+    JSLinearString* repl = replacement->ensureLinear(cx);
+    if (!repl)
+        return nullptr;
+
+    rdata.setReplacementString(repl);
+
+    if (!rdata.g.initRegExp(cx, regexp))
+        return nullptr;
+
+    return StrReplaceRegExp(cx, rdata);
+}
+
+static JSString*
+StrReplaceString(JSContext* cx, ReplaceData& rdata, const FlatMatch& fm)
+{
+    /*
+     * Note: we could optimize the text.length == pattern.length case if we wanted,
+     * even in the presence of dollar metachars.
+     */
+    if (rdata.dollarIndex != UINT32_MAX)
+        return BuildDollarReplacement(cx, rdata.str, rdata.repstr, rdata.dollarIndex, fm);
+    return BuildFlatReplacement(cx, rdata.str, rdata.repstr, fm);
+}
+
 template <typename StrChar, typename RepChar>
 static bool
 StrFlatReplaceGlobal(JSContext *cx, JSLinearString *str, JSLinearString *pat, JSLinearString *rep,
                      StringBuffer &sb)
 {
     MOZ_ASSERT(str->length() > 0);
 
     AutoCheckCannotGC nogc;
@@ -2295,152 +3555,376 @@ js::str_flat_replace_string(JSContext *c
 
     JSString *str = sb.finishString();
     if (!str)
         return nullptr;
 
     return str;
 }
 
+static const uint32_t ReplaceOptArg = 2;
+
 JSString*
 js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
                            HandleString replacement)
 {
-    RootedLinearString repl(cx, replacement->ensureLinear(cx));
+    ReplaceData rdata(cx);
+
+    rdata.str = string;
+    JSLinearString* repl = replacement->ensureLinear(cx);
     if (!repl)
         return nullptr;
-
-    RootedAtom pat(cx, AtomizeString(cx, pattern));
-    if (!pat)
+    rdata.setReplacementString(repl);
+
+    if (!rdata.g.init(cx, pattern))
         return nullptr;
-
-    size_t patternLength = pat->length();
-    int32_t match;
-    uint32_t dollarIndex;
-
-    {
-        AutoCheckCannotGC nogc;
-        dollarIndex = repl->hasLatin1Chars()
-                      ? FindDollarIndex(repl->latin1Chars(nogc), repl->length())
-                      : FindDollarIndex(repl->twoByteChars(nogc), repl->length());
+    const FlatMatch* fm = rdata.g.tryFlatMatch(cx, rdata.str, ReplaceOptArg, ReplaceOptArg, false);
+
+    if (fm->match() < 0)
+        return string;
+
+    return StrReplaceString(cx, rdata, *fm);
+}
+
+static inline bool
+str_replace_flat_lambda(JSContext* cx, const CallArgs& outerArgs, ReplaceData& rdata,
+                        const FlatMatch& fm)
+{
+    RootedString matchStr(cx, NewDependentString(cx, rdata.str, fm.match(), fm.patternLength()));
+    if (!matchStr)
+        return false;
+
+    /* lambda(matchStr, matchStart, textstr) */
+    static const uint32_t lambdaArgc = 3;
+    if (!rdata.fig.args().init(lambdaArgc))
+        return false;
+
+    RootedValue lambda(cx, ObjectValue(*rdata.lambda));
+    RootedValue thisv(cx, UndefinedValue());
+    RootedValue rval(cx);
+
+    CallArgs& args = rdata.fig.args();
+    Value* sp = args.array();
+    sp[0].setString(matchStr);
+    sp[1].setInt32(fm.match());
+    sp[2].setString(rdata.str);
+
+    if (!rdata.fig.call(cx, lambda, thisv, &rval))
+        return false;
+
+    RootedString repstr(cx, ToString<CanGC>(cx, rval));
+    if (!repstr)
+        return false;
+
+    RootedString leftSide(cx, NewDependentString(cx, rdata.str, 0, fm.match()));
+    if (!leftSide)
+        return false;
+
+    size_t matchLimit = fm.match() + fm.patternLength();
+    RootedString rightSide(cx, NewDependentString(cx, rdata.str, matchLimit,
+                                                  rdata.str->length() - matchLimit));
+    if (!rightSide)
+        return false;
+
+    RopeBuilder builder(cx);
+    if (!(builder.append(leftSide) &&
+          builder.append(repstr) &&
+          builder.append(rightSide))) {
+        return false;
     }
 
+    outerArgs.rval().setString(builder.result());
+    return true;
+}
+
+/*
+ * Pattern match the script to check if it is is indexing into a particular
+ * object, e.g. 'function(a) { return b[a]; }'. Avoid calling the script in
+ * such cases, which are used by javascript packers (particularly the popular
+ * Dean Edwards packer) to efficiently encode large scripts. We only handle the
+ * code patterns generated by such packers here.
+ */
+static bool
+LambdaIsGetElem(JSContext* cx, JSObject& lambda, MutableHandleNativeObject pobj)
+{
+    if (!lambda.is<JSFunction>())
+        return true;
+
+    RootedFunction fun(cx, &lambda.as<JSFunction>());
+    if (!fun->isInterpreted() || fun->isClassConstructor())
+        return true;
+
+    JSScript* script = fun->getOrCreateScript(cx);
+    if (!script)
+        return false;
+
+    jsbytecode* pc = script->code();
+
     /*
-     * |string| could be a rope, so we want to avoid flattening it for as
-     * long as possible.
+     * JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
+     * Rule out the (unlikely) possibility of a function with a call object
+     * since it would make our scope walk off by 1.
      */
-    if (string->isRope()) {
-        if (!RopeMatch(cx, &string->asRope(), pat, &match))
-            return nullptr;
+    if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->needsCallObject())
+        return true;
+    ScopeCoordinate sc(pc);
+    ScopeObject* scope = &fun->environment()->as<ScopeObject>();
+    for (unsigned i = 0; i < sc.hops(); ++i)
+        scope = &scope->enclosingScope().as<ScopeObject>();
+    Value b = scope->aliasedVar(sc);
+    pc += JSOP_GETALIASEDVAR_LENGTH;
+
+    /* Look for 'a' to be the lambda's first argument. */
+    if (JSOp(*pc) != JSOP_GETARG || GET_ARGNO(pc) != 0)
+        return true;
+    pc += JSOP_GETARG_LENGTH;
+
+    /* 'b[a]' */
+    if (JSOp(*pc) != JSOP_GETELEM)
+        return true;
+    pc += JSOP_GETELEM_LENGTH;
+
+    /* 'return b[a]' */
+    if (JSOp(*pc) != JSOP_RETURN)
+        return true;
+
+    /* 'b' must behave like a normal object. */
+    if (!b.isObject())
+        return true;
+
+    JSObject& bobj = b.toObject();
+    const Class* clasp = bobj.getClass();
+    if (!clasp->isNative() || clasp->getOpsLookupProperty() || clasp->getOpsGetProperty())
+        return true;
+
+    pobj.set(&bobj.as<NativeObject>());
+    return true;
+}
+
+bool
+js::str_replace(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    ReplaceData rdata(cx);
+    rdata.str = ThisToStringForStringProto(cx, args);
+    if (!rdata.str)
+        return false;
+
+    if (!rdata.g.init(cx, args))
+        return false;
+
+    /* Extract replacement string/function. */
+    if (args.length() >= ReplaceOptArg && IsCallable(args[1])) {
+        rdata.setReplacementFunction(&args[1].toObject());
+
+        if (!LambdaIsGetElem(cx, *rdata.lambda, &rdata.elembase))
+            return false;
     } else {
-        match = StringMatch(&string->asLinear(), pat, 0);
+        JSLinearString* string = ArgToRootedString(cx, args, 1);
+        if (!string)
+            return false;
+
+        rdata.setReplacementString(string);
     }
 
-    if (match < 0)
-        return string;
-
-    if (dollarIndex != UINT32_MAX)
-        return BuildDollarReplacement(cx, string, repl, dollarIndex, match, patternLength);
-    return BuildFlatReplacement(cx, string, repl, match, patternLength);
+    rdata.fig.initFunction(ObjectOrNullValue(rdata.lambda));
+
+    /*
+     * Unlike its |String.prototype| brethren, |replace| doesn't convert
+     * its input to a regular expression. (Even if it contains metachars.)
+     *
+     * However, if the user invokes our (non-standard) |flags| argument
+     * extension then we revert to creating a regular expression. Note that
+     * this is observable behavior through the side-effect mutation of the
+     * |RegExp| statics.
+     */
+
+    const FlatMatch* fm = rdata.g.tryFlatMatch(cx, rdata.str, ReplaceOptArg, args.length(), false);
+
+    if (!fm) {
+        if (cx->isExceptionPending())  /* oom in RopeMatch in tryFlatMatch */
+            return false;
+        return str_replace_regexp(cx, args, rdata);
+    }
+
+    if (fm->match() < 0) {
+        args.rval().setString(rdata.str);
+        return true;
+    }
+
+    if (rdata.lambda)
+        return str_replace_flat_lambda(cx, args, rdata, *fm);
+
+    JSString* res = StrReplaceString(cx, rdata, *fm);
+    if (!res)
+        return false;
+
+    args.rval().setString(res);
+    return true;
 }
 
-// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
+namespace {
+
+class SplitMatchResult {
+    size_t endIndex_;
+    size_t length_;
+
+  public:
+    void setFailure() {
+        JS_STATIC_ASSERT(SIZE_MAX > JSString::MAX_LENGTH);
+        endIndex_ = SIZE_MAX;
+    }
+    bool isFailure() const {
+        return endIndex_ == SIZE_MAX;
+    }
+    size_t endIndex() const {
+        MOZ_ASSERT(!isFailure());
+        return endIndex_;
+    }
+    size_t length() const {
+        MOZ_ASSERT(!isFailure());
+        return length_;
+    }
+    void setResult(size_t length, size_t endIndex) {
+        length_ = length;
+        endIndex_ = endIndex;
+    }
+};
+
+} /* anonymous namespace */
+
+template<class Matcher>
 static JSObject*
-SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep,
-            HandleObjectGroup group)
+SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch,
+            HandleObjectGroup group, bool unicode)
 {
     size_t strLength = str->length();
-    size_t sepLength = sep->length();
-    MOZ_ASSERT(sepLength != 0);
-
-    // Step 12.
+    SplitMatchResult result;
+
+    /* Step 11. */
     if (strLength == 0) {
-        // Step 12.a.
-        int match = StringMatch(str, sep, 0);
-
-        // Step 12.b.
-        if (match != -1)
+        if (!splitMatch(cx, str, 0, &result))
+            return nullptr;
+
+        /*
+         * NB: Unlike in the non-empty string case, it's perfectly fine
+         *     (indeed the spec requires it) if we match at the end of the
+         *     string.  Thus these cases should hold:
+         *
+         *   var a = "".split("");
+         *   assertEq(a.length, 0);
+         *   var b = "".split(/.?/);
+         *   assertEq(b.length, 0);
+         */
+        if (!result.isFailure())
             return NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
 
-        // Steps 12.c-e.
         RootedValue v(cx, StringValue(str));
         return NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
     }
 
-    // Step 3 (reordered).
-    AutoValueVector splits(cx);
-
-    // Step 8 (reordered).
+    /* Step 12. */
     size_t lastEndIndex = 0;
-
-    // Step 13.
     size_t index = 0;
 
-    // Step 14.
-    while (index != strLength) {
-        // Step 14.a.
-        int match = StringMatch(str, sep, index);
-
-        // Step 14.b.
-        //
-        // Our match algorithm differs from the spec in that it returns the
-        // next index at which a match happens.  If no match happens we're
-        // done.
-        //
-        // But what if the match is at the end of the string (and the string is
-        // not empty)?  Per 14.c.i this shouldn't be a match, so we have to
-        // specially exclude it.  Thus this case should hold:
-        //
-        //   var a = "abc".split(/\b/);
-        //   assertEq(a.length, 1);
-        //   assertEq(a[0], "abc");
-        if (match == -1)
+    /* Step 13. */
+    AutoValueVector splits(cx);
+
+    while (index < strLength) {
+        /* Step 13(a). */
+        if (!splitMatch(cx, str, index, &result))
+            return nullptr;
+
+        /*
+         * Step 13(b).
+         *
+         * Our match algorithm differs from the spec in that it returns the
+         * next index at which a match happens.  If no match happens we're
+         * done.
+         *
+         * But what if the match is at the end of the string (and the string is
+         * not empty)?  Per 13(c)(ii) this shouldn't be a match, so we have to
+         * specially exclude it.  Thus this case should hold:
+         *
+         *   var a = "abc".split(/\b/);
+         *   assertEq(a.length, 1);
+         *   assertEq(a[0], "abc");
+         */
+        if (result.isFailure())
             break;
 
-        // Step 14.c.
-        size_t endIndex = match + sepLength;
-
-        // Step 14.c.i.
+        /* Step 13(c)(i). */
+        size_t sepLength = result.length();
+        size_t endIndex = result.endIndex();
+        if (sepLength == 0 && endIndex == strLength)
+            break;
+
+        /* Step 13(c)(ii). */
         if (endIndex == lastEndIndex) {
-            index++;
+            index = AdvanceStringIndex(str, strLength, index, unicode);
             continue;
         }
 
-        // Step 14.c.ii.
+        /* Step 13(c)(iii). */
         MOZ_ASSERT(lastEndIndex < endIndex);
         MOZ_ASSERT(sepLength <= strLength);
         MOZ_ASSERT(lastEndIndex + sepLength <= endIndex);
 
-        // Step 14.c.ii.1.
+        /* Steps 13(c)(iii)(1-3). */
         size_t subLength = size_t(endIndex - sepLength - lastEndIndex);
         JSString* sub = NewDependentString(cx, str, lastEndIndex, subLength);
 
-        // Steps 14.c.ii.2-4.
         if (!sub || !splits.append(StringValue(sub)))
             return nullptr;
 
-        // Step 14.c.ii.5.
+        /* Step 13(c)(iii)(4). */
         if (splits.length() == limit)
             return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
 
-        // Step 14.c.ii.6.
-        index = endIndex;
-
-        // Step 14.c.ii.7.
-        lastEndIndex = index;
+        /* Step 13(c)(iii)(5). */
+        lastEndIndex = endIndex;
+
+        /* Step 13(c)(iii)(6-7). */
+        if (Matcher::returnsCaptures) {
+            RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+            if (!res)
+                return nullptr;
+
+            const MatchPairs& matches = res->getMatches();
+            for (size_t i = 0; i < matches.parenCount(); i++) {
+                /* Steps 13(c)(iii)(7)(a-c). */
+                if (!matches[i + 1].isUndefined()) {
+                    JSSubString parsub;
+                    res->getParen(i + 1, &parsub);
+                    sub = NewDependentString(cx, parsub.base, parsub.offset, parsub.length);
+                    if (!sub || !splits.append(StringValue(sub)))
+                        return nullptr;
+                } else {
+                    if (!splits.append(UndefinedValue()))
+                        return nullptr;
+                }
+
+                /* Step 13(c)(iii)(7)(d). */
+                if (splits.length() == limit)
+                    return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
+            }
+        }
+
+        /* Step 13(c)(iii)(8). */
+        index = lastEndIndex;
     }
 
-    // Step 15.
+    /* Steps 14-15. */
     JSString* sub = NewDependentString(cx, str, lastEndIndex, strLength - lastEndIndex);
 
-    // Steps 16-17.
     if (!sub || !splits.append(StringValue(sub)))
         return nullptr;
 
-    // Step 18.
+    /* Step 16. */
     return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
 }
 
 // Fast-path for splitting a string into a character array via split("").
 static JSObject*
 CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group)
 {
     size_t strLength = str->length();
@@ -2459,33 +3943,198 @@ CharSplitHelper(JSContext* cx, HandleLin
         if (!sub)
             return nullptr;
         splits.infallibleAppend(StringValue(sub));
     }
 
     return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
 }
 
-// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
+namespace {
+
+/*
+ * The SplitMatch operation from ES5 15.5.4.14 is implemented using different
+ * paths for regular expression and string separators.
+ *
+ * The algorithm differs from the spec in that the we return the next index at
+ * which a match happens.
+ */
+class SplitRegExpMatcher
+{
+    RegExpShared& re;
+    RegExpStatics* res;
+    bool sticky;
+
+  public:
+    SplitRegExpMatcher(RegExpShared& re, RegExpStatics* res) : re(re), res(res) {
+        sticky = re.sticky();
+    }
+
+    static const bool returnsCaptures = true;
+
+    bool operator()(JSContext* cx, HandleLinearString str, size_t index,
+                    SplitMatchResult* result) const
+    {
+        ScopedMatchPairs matches(&cx->tempLifoAlloc());
+        RegExpRunStatus status = re.execute(cx, str, index, sticky, &matches, nullptr);
+        if (status == RegExpRunStatus_Error)
+            return false;
+
+        if (status == RegExpRunStatus_Success_NotFound) {
+            result->setFailure();
+            return true;
+        }
+
+        if (!res->updateFromMatchPairs(cx, str, matches))
+            return false;
+
+        JSSubString sep;
+        res->getLastMatch(&sep);
+
+        result->setResult(sep.length, matches[0].limit);
+        return true;
+    }
+};
+
+class SplitStringMatcher
+{
+    RootedLinearString sep;
+
+  public:
+    SplitStringMatcher(JSContext* cx, HandleLinearString sep)
+      : sep(cx, sep)
+    {}
+
+    static const bool returnsCaptures = false;
+
+    bool operator()(JSContext* cx, JSLinearString* str, size_t index, SplitMatchResult* res) const
+    {
+        MOZ_ASSERT(index == 0 || index < str->length());
+        int match = StringMatch(str, sep, index);
+        if (match == -1)
+            res->setFailure();
+        else
+            res->setResult(sep->length(), match + sep->length());
+        return true;
+    }
+};
+
+} /* anonymous namespace */
+
+/* ES5 15.5.4.14 */
+bool
+js::str_split(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    /* Steps 1-2. */
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
+    if (!str)
+        return false;
+
+    RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
+    if (!group)
+        return false;
+
+    /* Step 5: Use the second argument as the split limit, if given. */
+    uint32_t limit;
+    if (args.hasDefined(1)) {
+        double d;
+        if (!ToNumber(cx, args[1], &d))
+            return false;
+        limit = ToUint32(d);
+    } else {
+        limit = UINT32_MAX;
+    }
+
+    /* Step 8. */
+    RegExpGuard re(cx);
+    RootedLinearString sepstr(cx);
+    bool sepDefined = args.hasDefined(0);
+    if (sepDefined) {
+        ESClassValue cls;
+        if (!GetClassOfValue(cx, args[0], &cls))
+            return false;
+
+        if (cls == ESClass_RegExp) {
+            RootedObject obj(cx, &args[0].toObject());
+            if (!RegExpToShared(cx, obj, &re))
+                return false;
+        } else {
+            sepstr = ArgToRootedString(cx, args, 0);
+            if (!sepstr)
+                return false;
+        }
+    }
+
+    /* Step 9. */
+    if (limit == 0) {
+        JSObject* aobj = NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
+        if (!aobj)
+            return false;
+        args.rval().setObject(*aobj);
+        return true;
+    }
+
+    /* Step 10. */
+    if (!sepDefined) {
+        RootedValue v(cx, StringValue(str));
+        JSObject* aobj = NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
+        if (!aobj)
+            return false;
+        args.rval().setObject(*aobj);
+        return true;
+    }
+    RootedLinearString linearStr(cx, str->ensureLinear(cx));
+    if (!linearStr)
+        return false;
+
+    /* Steps 11-15. */
+    RootedObject aobj(cx);
+    if (!re.initialized()) {
+        if (sepstr->length() == 0) {
+            aobj = CharSplitHelper(cx, linearStr, limit, group);
+        } else {
+            SplitStringMatcher matcher(cx, sepstr);
+            aobj = SplitHelper(cx, linearStr, limit, matcher, group, false);
+        }
+    } else {
+        RegExpStatics* res = cx->global()->getRegExpStatics(cx);
+        if (!res)
+            return false;
+        SplitRegExpMatcher matcher(*re, res);
+        aobj = SplitHelper(cx, linearStr, limit, matcher, group, re->unicode());
+    }
+    if (!aobj)
+        return false;
+
+    /* Step 16. */
+    MOZ_ASSERT(aobj->group() == group);
+    args.rval().setObject(*aobj);
+    return true;
+}
+
 JSObject*
-js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit)
-
+js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep)
 {
     RootedLinearString linearStr(cx, str->ensureLinear(cx));
     if (!linearStr)
         return nullptr;
 
     RootedLinearString linearSep(cx, sep->ensureLinear(cx));
     if (!linearSep)
         return nullptr;
 
+    uint32_t limit = UINT32_MAX;
+
     if (linearSep->length() == 0)
         return CharSplitHelper(cx, linearStr, limit, group);
 
-    return SplitHelper(cx, linearStr, limit, linearSep, group);
+    SplitStringMatcher matcher(cx, linearSep);
+    return SplitHelper(cx, linearStr, limit, matcher, group, false);
 }
 
 /*
  * Python-esque sequence operations.
  */
 bool
 js::str_concat(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2551,20 +4200,20 @@ static const JSFunctionSpec string_metho
     JS_FN("localeCompare",     str_localeCompare,     1,0),
 #endif
     JS_SELF_HOSTED_FN("repeat", "String_repeat",      1,0),
 #if EXPOSE_INTL_API
     JS_FN("normalize",         str_normalize,         0,0),
 #endif
 
     /* Perl-ish methods (search is actually Python-esque). */
-    JS_SELF_HOSTED_FN("match", "String_match",        1,0),
-    JS_SELF_HOSTED_FN("search", "String_search",      1,0),
-    JS_SELF_HOSTED_FN("replace", "String_replace",    2,0),
-    JS_SELF_HOSTED_FN("split",  "String_split",       2,0),
+    JS_FN("match",             str_match,             1,0),
+    JS_FN("search",            str_search,            1,0),
+    JS_INLINABLE_FN("replace", str_replace,           2,0, StringReplace),
+    JS_INLINABLE_FN("split",   str_split,             2,0, StringSplit),
     JS_SELF_HOSTED_FN("substr", "String_substr",      2,0),
 
     /* Python-esque sequence methods. */
     JS_FN("concat",            str_concat,            1,0),
     JS_SELF_HOSTED_FN("slice", "String_slice",        2,0),
 
     /* HTML string methods. */
     JS_SELF_HOSTED_FN("bold",     "String_bold",       0,0),
@@ -2706,20 +4355,20 @@ static const JSFunctionSpec string_stati
     JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
 
     JS_SELF_HOSTED_FN("fromCodePoint",   "String_static_fromCodePoint", 1,0),
     JS_SELF_HOSTED_FN("raw",             "String_static_raw",           2,JSFUN_HAS_REST),
     JS_SELF_HOSTED_FN("substring",       "String_static_substring",     3,0),
     JS_SELF_HOSTED_FN("substr",          "String_static_substr",        3,0),
     JS_SELF_HOSTED_FN("slice",           "String_static_slice",         3,0),
 
-    JS_SELF_HOSTED_FN("match",           "String_generic_match",        2,0),
-    JS_SELF_HOSTED_FN("replace",         "String_generic_replace",      3,0),
-    JS_SELF_HOSTED_FN("search",          "String_generic_search",       2,0),
-    JS_SELF_HOSTED_FN("split",           "String_generic_split",        3,0),
+    JS_SELF_HOSTED_FN("match",           "String_static_match",        2,0),
+    JS_SELF_HOSTED_FN("replace",         "String_static_replace",      3,0),
+    JS_SELF_HOSTED_FN("search",          "String_static_search",       2,0),
+    JS_SELF_HOSTED_FN("split",           "String_static_split",        1,0),
 
     JS_SELF_HOSTED_FN("toLowerCase",     "String_static_toLowerCase",   1,0),
     JS_SELF_HOSTED_FN("toUpperCase",     "String_static_toUpperCase",   1,0),
     JS_SELF_HOSTED_FN("charAt",          "String_static_charAt",        2,0),
     JS_SELF_HOSTED_FN("charCodeAt",      "String_static_charCodeAt",    2,0),
     JS_SELF_HOSTED_FN("includes",        "String_static_includes",      2,0),
     JS_SELF_HOSTED_FN("indexOf",         "String_static_indexOf",       2,0),
     JS_SELF_HOSTED_FN("lastIndexOf",     "String_static_lastIndexOf",   2,0),
@@ -3768,161 +5417,8 @@ js::PutEscapedStringImpl(char* buffer, s
 
 template size_t
 js::PutEscapedString(char* buffer, size_t bufferSize, const Latin1Char* chars, size_t length,
                      uint32_t quote);
 
 template size_t
 js::PutEscapedString(char* buffer, size_t bufferSize, const char16_t* chars, size_t length,
                      uint32_t quote);
-
-static bool
-FlatStringMatchHelper(JSContext* cx, HandleString str, HandleString pattern, bool* isFlat, int32_t* match)
-{
-    RootedLinearString linearPattern(cx, pattern->ensureLinear(cx));
-    if (!linearPattern)
-        return false;
-
-    static const size_t MAX_FLAT_PAT_LEN = 256;
-    if (linearPattern->length() > MAX_FLAT_PAT_LEN || StringHasRegExpMetaChars(linearPattern)) {
-        *isFlat = false;
-        return true;
-    }
-
-    *isFlat = true;
-    if (str->isRope()) {
-        if (!RopeMatch(cx, &str->asRope(), linearPattern, match))
-            return false;
-    } else {
-        *match = StringMatch(&str->asLinear(), linearPattern);
-    }
-
-    return true;
-}
-
-static bool
-BuildFlatMatchArray(JSContext* cx, HandleString str, HandleString pattern, int32_t match,
-                    MutableHandleValue rval)
-{
-    if (match < 0) {
-        rval.setNull();
-        return true;
-    }
-
-    /* Get the templateObject that defines the shape and type of the output object */
-    JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
-    if (!templateObject)
-        return false;
-
-    RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, 1, templateObject));
-    if (!arr)
-        return false;
-
-    /* Store a Value for each pair. */
-    arr->setDenseInitializedLength(1);
-    arr->initDenseElement(0, StringValue(pattern));
-
-    /* Set the |index| property. (TemplateObject positions it in slot 0) */
-    arr->setSlot(0, Int32Value(match));
-
-    /* Set the |input| property. (TemplateObject positions it in slot 1) */
-    arr->setSlot(1, StringValue(str));
-
-#ifdef DEBUG
-    RootedValue test(cx);
-    RootedId id(cx, NameToId(cx->names().index));
-    if (!NativeGetProperty(cx, arr, id, &test))
-        return false;
-    MOZ_ASSERT(test == arr->getSlot(0));
-    id = NameToId(cx->names().input);
-    if (!NativeGetProperty(cx, arr, id, &test))
-        return false;
-    MOZ_ASSERT(test == arr->getSlot(1));
-#endif
-
-    rval.setObject(*arr);
-    return true;
-}
-
-#ifdef DEBUG
-static bool
-CallIsStringOptimizable(JSContext* cx, const char* name, bool* result)
-{
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedPropertyName propName(cx, atom->asPropertyName());
-
-    RootedValue funcVal(cx);
-    if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), propName, propName, 0, &funcVal))
-        return false;
-
-    FixedInvokeArgs<0> args(cx);
-
-    RootedValue rval(cx);
-    if (!Call(cx, funcVal, UndefinedHandleValue, args, &rval))
-        return false;
-
-    *result = rval.toBoolean();
-    return true;
-}
-#endif
-
-bool
-js::FlatStringMatch(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(args[0].isString());
-    MOZ_ASSERT(args[1].isString());
-#ifdef DEBUG
-    bool isOptimizable = false;
-    if (!CallIsStringOptimizable(cx, "IsStringMatchOptimizable", &isOptimizable))
-        return false;
-    MOZ_ASSERT(isOptimizable);
-#endif
-
-    RootedString str(cx,args[0].toString());
-    RootedString pattern(cx, args[1].toString());
-
-    bool isFlat = false;
-    int32_t match = 0;
-    if (!FlatStringMatchHelper(cx, str, pattern, &isFlat, &match))
-        return false;
-
-    if (!isFlat) {
-        args.rval().setUndefined();
-        return true;
-    }
-
-    return BuildFlatMatchArray(cx, str, pattern, match, args.rval());
-}
-
-bool
-js::FlatStringSearch(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-    MOZ_ASSERT(args[0].isString());
-    MOZ_ASSERT(args[1].isString());
-#ifdef DEBUG
-    bool isOptimizable = false;
-    if (!CallIsStringOptimizable(cx, "IsStringSearchOptimizable", &isOptimizable))
-        return false;
-    MOZ_ASSERT(isOptimizable);
-#endif
-
-    RootedString str(cx,args[0].toString());
-    RootedString pattern(cx, args[1].toString());
-
-    bool isFlat = false;
-    int32_t match = 0;
-    if (!FlatStringMatchHelper(cx, str, pattern, &isFlat, &match))
-        return false;
-
-    if (!isFlat) {
-        args.rval().setInt32(-2);
-        return true;
-    }
-
-    args.rval().setInt32(match);
-    return true;
-}
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -233,16 +233,23 @@ StringHasPattern(JSLinearString* text, c
 
 extern int
 StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start);
 
 /* Return true if the string contains a pattern at |start|. */
 extern bool
 HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start);
 
+template <typename CharT>
+extern bool
+HasRegExpMetaChars(const CharT* chars, size_t length);
+
+extern bool
+StringHasRegExpMetaChars(JSLinearString* str);
+
 template <typename Char1, typename Char2>
 inline bool
 EqualChars(const Char1* s1, const Char2* s2, size_t len);
 
 template <typename Char1>
 inline bool
 EqualChars(const Char1* s1, const Char1* s2, size_t len)
 {
@@ -301,16 +308,23 @@ CopyAndInflateChars(char16_t* dst, const
  * must to be initialized with the buffer size and will contain on return the
  * number of copied bytes.
  */
 template <typename CharT>
 extern bool
 DeflateStringToBuffer(JSContext* maybecx, const CharT* chars,
                       size_t charsLength, char* bytes, size_t* length);
 
+/*
+ * The String.prototype.replace fast-native entry point is exported for joined
+ * function optimization in js{interp,tracer}.cpp.
+ */
+extern bool
+str_replace(JSContext* cx, unsigned argc, js::Value* vp);
+
 extern bool
 str_fromCharCode(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_fromCharCode_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval);
 
 /* String methods exposed so they can be installed in the self-hosting global. */
 
@@ -452,32 +466,34 @@ inline bool
 FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
 {
     Fprinter out(fp);
     bool res = EscapedStringPrinter(out, chars, length, quote);
     out.finish();
     return res;
 }
 
+bool
+str_match(JSContext* cx, unsigned argc, Value* vp);
+
+bool
+str_search(JSContext* cx, unsigned argc, Value* vp);
+
+bool
+str_split(JSContext* cx, unsigned argc, Value* vp);
+
 JSObject*
-str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
-                 uint32_t limit);
+str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep);
 
 JSString *
 str_flat_replace_string(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString replacement);
 
 JSString*
 str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
                        HandleString replacement);
 
 extern bool
 StringConstructor(JSContext* cx, unsigned argc, Value* vp);
 
-extern bool
-FlatStringMatch(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool
-FlatStringSearch(JSContext* cx, unsigned argc, Value* vp);
-
 } /* namespace js */
 
 #endif /* jsstr_h */
--- a/js/src/tests/ecma_3/String/15.5.4.11.js
+++ b/js/src/tests/ecma_3/String/15.5.4.11.js
@@ -152,17 +152,17 @@ rex = /x/g, rex.lastIndex = 1;
 reportCompare(
   "yyy0",
   "xxx".replace(rex, "y") + rex.lastIndex,
   "Section 24"
 );
 
 rex = /y/, rex.lastIndex = 1;
 reportCompare(
-  "xxx0",
+  "xxx1",
   "xxx".replace(rex, "y") + rex.lastIndex,
   "Section 25"
 );
 
 rex = /y/g, rex.lastIndex = 1;
 reportCompare(
   "xxx0",
   "xxx".replace(rex, "y") + rex.lastIndex,
--- a/js/src/tests/ecma_5/String/split-01.js
+++ b/js/src/tests/ecma_5/String/split-01.js
@@ -20,17 +20,17 @@ function assertEqArr(a1, a2) {
     }
 }
 
 var order = "";
 var o1 = { toString: function() { order += "b"; return "-"; }};
 var o2 = { valueOf:  function() { order += "a"; return 1; }};
 var res = "xyz-xyz".split(o1, o2);
 
-assertEq(order, "ba");
+assertEq(order, "ab");
 assertEqArr(res, ["xyz"]);
 
 assertEqArr("".split(/.?/), []);
 assertEqArr("abc".split(/\b/), ["abc"]);
 
 assertEqArr("abc".split(/((()))./, 2), ["",""]);
 assertEqArr("abc".split(/((((()))))./, 9), ["","","","","","","","",""]);
 
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/RegExpExec-exec.js
+++ /dev/null
@@ -1,18 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "RegExpExec should throw if exec property of non-RegExp is not callable";
-
-print(BUGNUMBER + ": " + summary);
-
-for (var exec of [null, 0, false, undefined, ""]) {
-  // RegExp with non-callable exec
-  var re = /a/;
-  re.exec = exec;
-  RegExp.prototype[Symbol.match].call(re, "foo");
-
-  // non-RegExp with non-callable exec
-  assertThrowsInstanceOf(() => RegExp.prototype[Symbol.match].call({ exec }, "foo"),
-                         TypeError);
-}
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/RegExpExec-return.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "RegExpExec should throw if returned value is not an object nor null.";
-
-print(BUGNUMBER + ": " + summary);
-
-for (var ret of [null, {}, [], /a/]) {
-  assertEq(RegExp.prototype[Symbol.match].call({
-    get global() {
-      return false;
-    },
-    exec(S) {
-      return ret;
-    }
-  }, "foo"), ret);
-}
-
-for (ret of [undefined, 1, true, false, Symbol.iterator]) {
-  assertThrowsInstanceOf(() => {
-    RegExp.prototype[Symbol.match].call({
-      get global() {
-        return false;
-      },
-      exec(S) {
-        return ret;
-      }
-    }, "foo");
-  }, TypeError);
-}
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
--- a/js/src/tests/ecma_6/RegExp/constructor-constructor.js
+++ b/js/src/tests/ecma_6/RegExp/constructor-constructor.js
@@ -20,34 +20,16 @@ assertEq(RegExp(re) === re, false);
 assertEq(RegExp(re).toString(), re.toString());
 
 
 re = new Proxy(/a/, {
   get(that, name) {
     return that[name];
   }
 });
-assertEq(RegExp(re), re);
-re = new Proxy(/a/, {
-  get(that, name) {
-    if (name == "constructor") {
-      return function() {};
-    }
-    return that[name];
-  }
-});
-assertEq(RegExp(re) === re, false);
-re = new Proxy(/a/, {
-  get(that, name) {
-    if (name == Symbol.match) {
-      return undefined;
-    }
-    return that[name];
-  }
-});
 assertEq(RegExp(re) === re, false);
 
 re = new Proxy(g.eval(`/a/`), {
   get(that, name) {
     return that[name];
   }
 });
 assertEq(RegExp(re) === re, false);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/match-this.js
+++ /dev/null
@@ -1,12 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "RegExp.prototype[@@match] should check this value.";
-
-print(BUGNUMBER + ": " + summary);
-
-for (var v of [null, 1, true, undefined, "", Symbol.iterator]) {
-  assertThrowsInstanceOf(() => RegExp.prototype[Symbol.match].call(v),
-                         TypeError);
-}
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/match-trace.js
+++ /dev/null
@@ -1,147 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "Trace RegExp.prototype[@@match] behavior.";
-
-print(BUGNUMBER + ": " + summary);
-
-var n;
-var log;
-var target;
-var global;
-var unicode;
-var logProxy;
-
-var execResult;
-var lastIndexResult;
-var lastIndexExpected;
-
-function P(A) {
-  return new Proxy(A, {
-    get(that, name) {
-      if (logProxy)
-        log += "get:result[" + name + "],";
-      return that[name];
-    }
-  });
-}
-
-var myRegExp = {
-  get global() {
-    log += "get:global,";
-    return global;
-  },
-  get lastIndex() {
-    log += "get:lastIndex,";
-    return lastIndexResult[n];
-  },
-  set lastIndex(v) {
-    log += "set:lastIndex,";
-    assertEq(v, lastIndexExpected[n]);
-  },
-  get unicode() {
-    log += "get:unicode,";
-    return unicode;
-  },
-  get exec() {
-    log += "get:exec,";
-    return function(S) {
-      log += "call:exec,";
-      assertEq(S, target);
-      return execResult[n++];
-    };
-  },
-};
-
-function reset() {
-  n = 0;
-  log = "";
-  target = "abcAbcABC";
-  global = true;
-  unicode = false;
-  logProxy = true;
-}
-
-// Trace global with non-empty match.
-reset();
-execResult        = [    P(["abc"]), P(["ABC"]), null ];
-lastIndexResult   = [ ,  ,           ,                ];
-lastIndexExpected = [ 0, ,           ,                ];
-var ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
-assertEq(JSON.stringify(ret), `["abc","ABC"]`);
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec,get:result[0]," +
-         "get:exec,call:exec,get:result[0]," +
-         "get:exec,call:exec,");
-
-// Trace global with empty match.
-reset();
-execResult        = [    P([""]), P([""]), null ];
-lastIndexResult   = [ ,  4,       20,           ];
-lastIndexExpected = [ 0, 5,       21,           ];
-ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
-assertEq(JSON.stringify(ret), `["",""]`);
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,");
-
-// Trace global and unicode with empty match.
-// 1. not surrogate pair
-// 2. lead surrogate pair
-// 3. trail surrogate pair
-// 4. lead surrogate pair without trail surrogate pair
-// 5. index overflow
-reset();
-unicode = true;
-//        0123     4     5678
-target = "___\uD83D\uDC38___\uD83D";
-execResult        = [    P([""]), P([""]), P([""]), P([""]), P([""]), null ];
-lastIndexResult   = [ ,  2,       3,       4,       8,       9,            ];
-lastIndexExpected = [ 0, 3,       5,       5,       9,       10,           ];
-ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
-assertEq(JSON.stringify(ret), `["","","","",""]`);
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec,");
-
-// Trace global with no match.
-reset();
-execResult        = [    null ];
-lastIndexResult   = [ ,       ];
-lastIndexExpected = [ 0,      ];
-ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
-assertEq(ret, null);
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec,");
-
-// Trace non-global.
-reset();
-global = false;
-execResult        = [ P(["abc"]) ];
-lastIndexResult   = [];
-lastIndexExpected = [];
-ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
-// ret is the Proxy on non-global case, disable logging.
-logProxy = false;
-assertEq(JSON.stringify(ret), `["abc"]`);
-assertEq(log,
-         "get:global," +
-         "get:exec,call:exec,");
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/match.js
+++ /dev/null
@@ -1,36 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "Implement RegExp.prototype[@@match].";
-
-print(BUGNUMBER + ": " + summary);
-
-assertEq(RegExp.prototype[Symbol.match].name, "[Symbol.match]");
-assertEq(RegExp.prototype[Symbol.match].length, 1);
-var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, Symbol.match);
-assertEq(desc.configurable, true);
-assertEq(desc.enumerable, false);
-assertEq(desc.writable, true);
-
-var re = /a/;
-var v = re[Symbol.match]("abcAbcABC");
-assertEq(Array.isArray(v), true);
-assertEq(v.length, 1);
-assertEq(v[0], "a");
-
-re = /d/;
-v = re[Symbol.match]("abcAbcABC");
-assertEq(v, null);
-
-re = /a/ig;
-v = re[Symbol.match]("abcAbcABC");
-assertEq(Array.isArray(v), true);
-assertEq(v.length, 3);
-assertEq(v[0], "a");
-assertEq(v[1], "A");
-assertEq(v[2], "A");
-
-re = /d/g;
-v = re[Symbol.match]("abcAbcABC");
-assertEq(v, null);
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
--- a/js/src/tests/ecma_6/RegExp/replace-sticky-lastIndex.js
+++ b/js/src/tests/ecma_6/RegExp/replace-sticky-lastIndex.js
@@ -1,23 +1,25 @@
 var BUGNUMBER = 887016;
 var summary = "String.prototype.replace should do nothing if lastIndex is invalid for sticky RegExp";
 
 print(BUGNUMBER + ": " + summary);
 
-var re = /a/y;
-re.lastIndex = -1;
-assertEq("a".replace(re, "b"), "b");
-re.lastIndex = 0;
-assertEq("a".replace(re, "b"), "b");
-re.lastIndex = 1;
-assertEq("a".replace(re, "b"), "a");
-re.lastIndex = 2;
-assertEq("a".replace(re, "b"), "a");
-re.lastIndex = "foo";
-assertEq("a".replace(re, "b"), "b");
-re.lastIndex = "1";
-assertEq("a".replace(re, "b"), "a");
-re.lastIndex = {};
-assertEq("a".replace(re, "b"), "b");
+if (Symbol.replace) {
+    var re = /a/y;
+    re.lastIndex = -1;
+    assertEq("a".replace(re, "b"), "b");
+    re.lastIndex = 0;
+    assertEq("a".replace(re, "b"), "b");
+    re.lastIndex = 1;
+    assertEq("a".replace(re, "b"), "a");
+    re.lastIndex = 2;
+    assertEq("a".replace(re, "b"), "a");
+    re.lastIndex = "foo";
+    assertEq("a".replace(re, "b"), "b");
+    re.lastIndex = "1";
+    assertEq("a".replace(re, "b"), "a");
+    re.lastIndex = {};
+    assertEq("a".replace(re, "b"), "b");
+}
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/replace-sticky.js
+++ /dev/null
@@ -1,21 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "String.prototype.replace should use and update lastIndex if sticky flag is set";
-
-print(BUGNUMBER + ": " + summary);
-
-var input = "abcdeabcdeabcdefghij";
-var re = new RegExp("abcde", "y");
-re.test(input);
-assertEq(re.lastIndex, 5);
-var ret = input.replace(re, "ABCDE");
-assertEq(ret, "abcdeABCDEabcdefghij");
-assertEq(re.lastIndex, 10);
-ret = input.replace(re, "ABCDE");
-assertEq(ret, "abcdeabcdeABCDEfghij");
-assertEq(re.lastIndex, 15);
-ret = input.replace(re, "ABCDE");
-assertEq(ret, "abcdeabcdeabcdefghij");
-assertEq(re.lastIndex, 0);
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/replace-this.js
+++ /dev/null
@@ -1,12 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "RegExp.prototype[@@replace] should check |this| value.";
-
-print(BUGNUMBER + ": " + summary);
-
-for (var v of [null, 1, true, undefined, "", Symbol.iterator]) {
-  assertThrowsInstanceOf(() => RegExp.prototype[Symbol.replace].call(v),
-                         TypeError);
-}
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/replace-trace.js
+++ /dev/null
@@ -1,309 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "Trace RegExp.prototype[@@replace] behavior.";
-
-print(BUGNUMBER + ": " + summary);
-
-var n;
-var log;
-var target;
-var global;
-var unicode;
-
-var execResult;
-var lastIndexResult;
-var lastIndexExpected;
-
-var arraySetterObserved = false;
-function startObserve() {
-  for (var i = 0; i < 10; i++) {
-    Object.defineProperty(Array.prototype, i, {
-      set: function(v) {
-        arraySetterObserved = true;
-      },
-      configurable: true,
-    });
-  }
-}
-function stopObserve() {
-  for (var i = 0; i < 10; i++)
-    delete Array.prototype[i]
-}
-
-startObserve();
-
-function P(A, index, matched2) {
-  var i = 0;
-  A.index = index;
-  return new Proxy(A, {
-    get(that, name) {
-      log += "get:result[" + name + "],";
-
-      // Return a different value for 2nd access to result[0].
-      if (matched2 !== undefined && name == 0) {
-        if (i == 1) {
-          return matched2;
-        }
-        i++;
-      }
-
-      return that[name];
-    }
-  });
-}
-
-var myRegExp = {
-  get global() {
-    log += "get:global,";
-    return global;
-  },
-  get lastIndex() {
-    log += "get:lastIndex,";
-    return lastIndexResult[n];
-  },
-  set lastIndex(v) {
-    log += "set:lastIndex,";
-    assertEq(v, lastIndexExpected[n]);
-  },
-  get unicode() {
-    log += "get:unicode,";
-    return unicode;
-  },
-  get exec() {
-    log += "get:exec,";
-    return function(S) {
-      log += "call:exec,";
-      assertEq(S, target);
-      return execResult[n++];
-    };
-  },
-};
-
-function reset() {
-  n = 0;
-  log = "";
-  target = "abcAbcABC";
-  global = true;
-  unicode = false;
-  arraySetterObserved = false;
-}
-
-// Trace global with non-empty match.
-reset();
-execResult        = [    P(["bc"], 1), null ];
-lastIndexResult   = [ ,  ,                  ];
-lastIndexExpected = [ 0, ,                  ];
-var ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "_XYZ_");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "a_XYZ_AbcABC");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index],");
-
-// Trace global with empty match.
-reset();
-execResult        = [    P([""], 1), null ];
-lastIndexResult   = [ ,  5,               ];
-lastIndexExpected = [ 0, 6,               ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "_XYZ_");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "a_XYZ_bcAbcABC");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index],");
-
-// Trace global and unicode with empty match.
-// 1. not surrogate pair
-// 2. lead surrogate pair
-// 3. trail surrogate pair
-// 4. lead surrogate pair without trail surrogate pair
-// 5. index overflow
-reset();
-unicode = true;
-//        0123     4     5678
-target = "---\uD83D\uDC38---\uD83D";
-execResult        = [    P([""], 1), P([""], 2), P([""], 3), P([""], 4), P([""], 5), null ];
-lastIndexResult   = [ ,  2,          3,          4,          8,          9,               ];
-lastIndexExpected = [ 0, 3,          5,          5,          9,          10,              ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "_XYZ_");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "-_XYZ_-_XYZ_-_XYZ_\uD83D_XYZ_\uDC38_XYZ_---\uD83D");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[length],get:result[0],get:result[index],");
-
-// Trace global with captures and substitutions.
-reset();
-execResult        = [    P(["bc", "b", "c"], 1), null ];
-lastIndexResult   = [ ,  ,                            ];
-lastIndexExpected = [ 0, ,                            ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "[$&,$`,$',$1,$2,$3,$]");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "a[bc,a,AbcABC,b,c,$3,$]AbcABC");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[1],get:result[2],");
-
-// Trace global with empty match and captures and substitutions,
-// with different matched.
-reset();
-execResult        = [    P(["", "b", "c"], 1, "BC"), null ];
-lastIndexResult   = [ ,  5,                               ];
-lastIndexExpected = [ 0, 6,                               ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "[$&,$`,$',$1,$2,$3,$]");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "a[BC,a,AbcABC,b,c,$3,$]AbcABC");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[1],get:result[2],");
-
-// Trace global with empty match and captures and function,
-// with different matched.
-reset();
-execResult        = [    P(["", "b", "c"], 1, "BC"), null ];
-lastIndexResult   = [ ,  5,                               ];
-lastIndexExpected = [ 0, 6,                               ];
-function replaceFunc(...args) {
-  log += "call:replaceFunc,";
-  assertEq(args.length, 5);
-  assertEq(args[0], "BC");
-  assertEq(args[1], "b");
-  assertEq(args[2], "c");
-  assertEq(args[3], 1);
-  assertEq(args[4], target);
-  return "_ret_";
-}
-// This also tests RegExpStatics save/restore with no match.
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, replaceFunc);
-assertEq(arraySetterObserved, false);
-assertEq(ret, "a_ret_AbcABC");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:lastIndex,set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[1],get:result[2]," +
-         "call:replaceFunc,");
-
-// Trace global with non-empty match, move backwards.
-// 2nd match shouldn't be replaced.
-reset();
-execResult        = [    P(["X"], 5), P(["YZ"], 1), null ];
-lastIndexResult   = [ ,  ,            ,                  ];
-lastIndexExpected = [ 0, ,            ,                  ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "_XYZ_");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "abcAb_XYZ_ABC");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index]," +
-         "get:result[length],get:result[0],get:result[index],");
-
-// Trace global with non-empty match, position + matchLength overflows.
-reset();
-execResult        = [    P(["fooooooo"], 7), null ];
-lastIndexResult   = [ ,  ,                        ];
-lastIndexExpected = [ 0, ,                        ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "_XYZ_");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "abcAbcA_XYZ_");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index],");
-
-// Trace global with non-empty match, position overflows.
-reset();
-execResult        = [    P(["fooooooo"], 12), null ];
-lastIndexResult   = [ ,  ,                         ];
-lastIndexExpected = [ 0, ,                         ];
-ret = RegExp.prototype[Symbol.replace].call(myRegExp, target, "_XYZ_");
-assertEq(arraySetterObserved, false);
-assertEq(ret, "abcAbcABC_XYZ_");
-assertEq(log,
-         "get:global," +
-         "get:unicode," +
-         "set:lastIndex," +
-         "get:exec,call:exec," +
-         "get:result[0]," +
-         "get:exec,call:exec," +
-         "get:result[length],get:result[0],get:result[index],");
-
-// Trace non-global.
-reset();
-global = false;