Backed out changeset 01da4d30fd11 (bug 887016)
authorTooru Fujisawa <arai_a@mac.com>
Mon, 28 Mar 2016 06:49:53 +0900
changeset 290648 5676c7b622c71064d62f8e3a57c7d35d1043f9bc
parent 290647 d44ccce05064d63cd96efd511694bc1a28d94646
child 290649 0bfefec1be82ae534a69701d0e99ab664ea209bb
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs887016
milestone48.0a1
backs out01da4d30fd114f532009a77acfc663fd54699502
Backed out changeset 01da4d30fd11 (bug 887016)
js/src/builtin/Intl.js
js/src/builtin/RegExp.cpp
js/src/builtin/RegExp.js
js/src/builtin/String.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/InlinableNatives.h
js/src/jit/IonBuilder.h
js/src/jit/Lowering.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/Recover.cpp
js/src/jit/SharedIC.cpp
js/src/jsapi.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/tests/ecma_5/String/split-01.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/split.js
js/src/tests/ecma_6/Symbol/well-known.js
js/src/vm/CommonPropertyNames.h
js/src/vm/GlobalObject.cpp
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
@@ -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 = callFunction(String_split, 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 = callFunction(String_split, 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
@@ -644,17 +644,16 @@ const JSFunctionSpec js::regexp_methods[
 #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);                      \
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -481,194 +481,16 @@ function RegExpSearch(string) {
     // Step 8.
     if (result === null)
         return -1;
 
     // Step 9.
     return result.index;
 }
 
-function IsRegExpSplitOptimizable(C) {
-    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");
-
-    // Steps 8-9.
-    var newFlags;
-    if (callFunction(std_String_includes, flags, "y"))
-        newFlags = flags;
-    else
-        newFlags = flags + "y";
-
-    // Step 10.
-    var 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 14.
-    var size = S.length;
-
-    // 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;
-    }
-
-    var optimizable = IsRegExpSplitOptimizable(C);
-
-    // 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.
-            // Directly call RegExpMatcher to ignore flags and find first match.
-            z = RegExpMatcher(splitter, S, q, false);
-
-            // 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.
-    _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");
 
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -226,93 +226,16 @@ function String_search(regexp) {
 }
 
 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;
--- 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"
@@ -5657,19 +5656,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;
         }
 
@@ -5712,29 +5709,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,
@@ -5751,17 +5748,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;
@@ -5999,30 +5996,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.
@@ -6035,17 +6033,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;
@@ -6927,82 +6925,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);
@@ -7015,17 +7008,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
@@ -2954,81 +2954,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
@@ -7227,23 +7227,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/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -67,22 +67,22 @@
                                     \
     _(RegExpMatcher)                \
     _(RegExpTester)                 \
     _(IsRegExpObject)               \
     _(RegExpPrototypeOptimizable)   \
     _(RegExpInstanceOptimizable)    \
                                     \
     _(String)                       \
+    _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
                                     \
     _(IntrinsicStringReplaceString) \
-    _(IntrinsicStringSplitString)   \
                                     \
     _(ObjectCreate)                 \
                                     \
     _(SimdInt32x4)                  \
     _(SimdUint32x4)                 \
     _(SimdFloat32x4)                \
     _(SimdBool32x4)                 \
                                     \
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -806,25 +806,25 @@ class IonBuilder
     InliningStatus inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType);
     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);
 
     // RegExp intrinsics.
     InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
     InliningStatus inlineRegExpTester(CallInfo& callInfo);
     InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
     InliningStatus inlineRegExpPrototypeOptimizable(CallInfo& callInfo);
     InliningStatus inlineRegExpInstanceOptimizable(CallInfo& callInfo);
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2182,16 +2182,22 @@ 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))
+    {
+        return false;
+    }
+
     return true;
 }
 
 
 static bool
 MustCloneRegExp(MRegExp* regexp)
 {
     if (regexp->mustClone())
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -15,17 +15,16 @@
 #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"
 
@@ -187,28 +186,28 @@ IonBuilder::inlineNativeCall(CallInfo& c
       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);
 
       // Object natives.
       case InlinableNative::ObjectCreate:
         return inlineObjectCreate(callInfo);
 
       // SIMD natives.
       case InlinableNative::SimdInt32x4:
         return inlineSimd(callInfo, target, SimdType::Int32x4);
@@ -1465,47 +1464,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())
-        return InliningStatus_NotInlined;
-
-    MConstant* strval = callInfo.getArg(0)->toConstant();
-    if (strval->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    MConstant* sepval = callInfo.getArg(1)->toConstant();
+    MConstant* argval = callInfo.getArg(0)->toConstant();
+    if (argval->type() != MIRType_String)
+        return InliningStatus_NotInlined;
+
+    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))
-        return InliningStatus_NotInlined;
-
-    MOZ_ASSERT(stringStr);
-    MOZ_ASSERT(stringSep);
+    if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject))
+        return InliningStatus_NotInlined;
+
+    MOZ_ASSERT(stringThis);
+    MOZ_ASSERT(stringArg);
     MOZ_ASSERT(templateObject);
 
-    if (strval->toString() != stringStr)
-        return InliningStatus_NotInlined;
-
-    if (sepval->toString() != stringSep)
+    if (strval->toString() != stringThis)
+        return InliningStatus_NotInlined;
+
+    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);
@@ -1568,37 +1567,33 @@ IonBuilder::inlineConstantStringSplitStr
     MInstruction* setLength = setInitializedLength(ins, 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)
-        return InliningStatus_NotInlined;
-
-    if (sepArg->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    IonBuilder::InliningStatus resultConstStringSplit = inlineConstantStringSplitString(callInfo);
+    if (callInfo.thisArg()->type() != MIRType_String)
+        return InliningStatus_NotInlined;
+    if (callInfo.getArg(0)->type() != MIRType_String)
+        return InliningStatus_NotInlined;
+
+    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);
@@ -1610,18 +1605,18 @@ 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)
--- 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;
 }
 
--- 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/jsapi.h
+++ b/js/src/jsapi.h
@@ -4864,17 +4864,16 @@ GetSymbolDescription(HandleSymbol symbol
 
 /* Well-known symbols. */
 #define JS_FOR_EACH_WELL_KNOWN_SYMBOL(macro) \
     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/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1521,18 +1521,18 @@ RopeMatch(JSContext* cx, JSRope* text, J
         else
             *match = RopeMatchImpl<char16_t>(nogc, strings, pat->twoByteChars(nogc), patLen);
     }
 
     return true;
 }
 
 /* ES6 draft rc4 21.1.3.7. */
-bool
-js::str_includes(JSContext* cx, unsigned argc, Value* vp)
+static bool
+str_includes(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1, 2, and 3
     RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
@@ -1961,16 +1961,51 @@ str_trimLeft(JSContext* cx, unsigned arg
 }
 
 static bool
 str_trimRight(JSContext* cx, unsigned argc, Value* vp)
 {
     return TrimString(cx, vp, false, 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;
+}
+
 // 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;
 
@@ -2343,110 +2378,172 @@ js::str_replace_string_raw(JSContext* cx
     if (match < 0)
         return string;
 
     if (dollarIndex != UINT32_MAX)
         return BuildDollarReplacement(cx, string, repl, dollarIndex, match, patternLength);
     return BuildFlatReplacement(cx, string, repl, match, patternLength);
 }
 
-// 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();
@@ -2465,33 +2562,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.
  */
 static bool
 str_concat(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2559,17 +2821,17 @@ static const JSFunctionSpec string_metho
 #if EXPOSE_INTL_API
     JS_FN("normalize",         str_normalize,         0,JSFUN_GENERIC_NATIVE),
 #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_INLINABLE_FN("split",   str_split,             2,JSFUN_GENERIC_NATIVE, StringSplit),
     JS_SELF_HOSTED_FN("substr", "String_substr",      2,0),
 
     /* Python-esque sequence methods. */
     JS_FN("concat",            str_concat,            1,JSFUN_GENERIC_NATIVE),
     JS_SELF_HOSTED_FN("slice", "String_slice",        2,0),
 
     /* HTML string methods. */
     JS_SELF_HOSTED_FN("bold",     "String_bold",       0,0),
@@ -2714,17 +2976,16 @@ static const JSFunctionSpec string_stati
     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),
 
     // This must be at the end because of bug 853075: functions listed after
     // self-hosted methods aren't available in self-hosted code.
 #if EXPOSE_INTL_API
     JS_SELF_HOSTED_FN("localeCompare",   "String_static_localeCompare", 2,0),
 #endif
     JS_FS_END
 };
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -305,19 +305,16 @@ 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. */
 
 extern bool
-str_includes(JSContext* cx, unsigned argc, Value* vp);
-
-extern bool
 str_indexOf(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_lastIndexOf(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_startsWith(JSContext* cx, unsigned argc, Value* vp);
 
@@ -414,19 +411,21 @@ 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_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);
--- 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/split-this.js
+++ /dev/null
@@ -1,12 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "RegExp.prototype[@@split] should check this value.";
-
-print(BUGNUMBER + ": " + summary);
-
-for (var v of [null, 1, true, undefined, "", Symbol.iterator]) {
-  assertThrowsInstanceOf(() => RegExp.prototype[Symbol.split].call(v),
-                         TypeError);
-}
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/split-trace.js
+++ /dev/null
@@ -1,218 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "Trace RegExp.prototype[@@split] behavior.";
-
-print(BUGNUMBER + ": " + summary);
-
-var n;
-var log;
-var target;
-var flags;
-var expectedFlags;
-
-var execResult;
-var lastIndexResult;
-var lastIndexExpected;
-
-var arraySetterObserved = false;
-for (var i = 0; i < 10; i++) {
-  Object.defineProperty(Array.prototype, i, {
-    set: function(v) {
-      arraySetterObserved = true;
-    }
-  });
-}
-
-function P(A) {
-  return new Proxy(A, {
-    get(that, name) {
-      log += "get:result[" + name + "],";
-      return that[name];
-    }
-  });
-}
-
-var myRegExp = {
-  get constructor() {
-    log += "get:constructor,";
-    return {
-      get [Symbol.species]() {
-        log += "get:species,";
-        return function(pattern, flags) {
-          assertEq(pattern, myRegExp);
-          assertEq(flags, expectedFlags);
-          log += "call:constructor,";
-          return {
-            get lastIndex() {
-              log += "get:lastIndex,";
-              return lastIndexResult[n];
-            },
-            set lastIndex(v) {
-              log += "set:lastIndex,";
-              assertEq(v, lastIndexExpected[n]);
-            },
-            get flags() {
-              log += "get:flags,";
-              return flags;
-            },
-            get exec() {
-              log += "get:exec,";
-              return function(S) {
-                log += "call:exec,";
-                assertEq(S, target);
-                return execResult[n++];
-              };
-            },
-          };
-        };
-      }
-    };
-  },
-  get flags() {
-    log += "get:flags,";
-    return flags;
-  },
-};
-
-function reset() {
-  n = 0;
-  log = "";
-  target = "abcde";
-  flags = "";
-  expectedFlags = "y";
-  arraySetterObserved = false;
-}
-
-// Trace match and no match.
-reset();
-execResult        = [    null, P(["b"]), null, P(["d"]), null ];
-lastIndexResult   = [ ,  ,     2,        ,     4,        ,    ];
-lastIndexExpected = [ 0, 1,    2,        3,    4,             ];
-var ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
-assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `["a","c","e"]`);
-assertEq(log,
-         "get:constructor," +
-         "get:species," +
-         "get:flags," +
-         "call:constructor," +
-         "set:lastIndex,get:exec,call:exec," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "get:result[length]," +
-         "set:lastIndex,get:exec,call:exec," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "get:result[length]," +
-         "set:lastIndex,get:exec,call:exec,");
-
-// Trace non-empty flags, empty target, no match.
-reset();
-flags = "iy";
-expectedFlags = "iy";
-target = "";
-execResult        = [ null ];
-lastIndexResult   = [];
-lastIndexExpected = [];
-ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
-assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `[""]`);
-assertEq(log,
-         "get:constructor," +
-         "get:species," +
-         "get:flags," +
-         "call:constructor," +
-         "get:exec,call:exec,");
-
-// Trace empty target, match.
-reset();
-target = "";
-execResult        = [ P([""]) ];
-lastIndexResult   = [];
-lastIndexExpected = [];
-ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
-assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `[]`);
-assertEq(log,
-         "get:constructor," +
-         "get:species," +
-         "get:flags," +
-         "call:constructor," +
-         "get:exec,call:exec,");
-
-// Trace captures.
-reset();
-target = "abc";
-execResult        = [    null, P(["b", "X", "YZ"]), null ];
-lastIndexResult   = [ ,  ,     2,                   ,    ];
-lastIndexExpected = [ 0, 1,    2,                        ];
-ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
-assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `["a","X","YZ","c"]`);
-assertEq(log,
-         "get:constructor," +
-         "get:species," +
-         "get:flags," +
-         "call:constructor," +
-         "set:lastIndex,get:exec,call:exec," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "get:result[length]," +
-         "get:result[1],get:result[2]," +
-         "set:lastIndex,get:exec,call:exec,");
-
-// Trace unicode.
-// 1. not surrogate pair
-// 2. lead surrogate pair
-// 3. trail surrogate pair
-// 4. lead surrogate pair without trail surrogate pair
-// 5. index overflow
-reset();
-flags = "u";
-expectedFlags = "uy";
-target = "-\uD83D\uDC38\uDC38\uD83D";
-execResult        = [    null, null, null, null ];
-lastIndexResult   = [ ,  ,     ,     ,     ,    ];
-lastIndexExpected = [ 0, 1,    3,    4,         ];
-ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
-assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `["-\uD83D\uDC38\uDC38\uD83D"]`);
-assertEq(log,
-         "get:constructor," +
-         "get:species," +
-         "get:flags," +
-         "call:constructor," +
-         "set:lastIndex,get:exec,call:exec," +
-         "set:lastIndex,get:exec,call:exec," +
-         "set:lastIndex,get:exec,call:exec," +
-         "set:lastIndex,get:exec,call:exec,");
-
-// Trace unicode, match, same position and different position.
-reset();
-flags = "u";
-expectedFlags = "uy";
-target = "-\uD83D\uDC38\uDC38\uD83D";
-var E = P(["", "X"]);
-execResult        = [    E, E, E, E, E, E, E ];
-lastIndexResult   = [ ,  0, 1, 1, 3, 3, 4, 4 ];
-lastIndexExpected = [ 0, 1, 1, 3, 3, 4, 4,   ];
-ret = RegExp.prototype[Symbol.split].call(myRegExp, target);
-assertEq(arraySetterObserved, false);
-assertEq(JSON.stringify(ret), `["-","X","\uD83D\uDC38","X","\uDC38","X","\uD83D"]`);
-assertEq(log,
-         "get:constructor," +
-         "get:species," +
-         "get:flags," +
-         "call:constructor," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "get:result[length]," +
-         "get:result[1]," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "get:result[length]," +
-         "get:result[1]," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex," +
-         "get:result[length]," +
-         "get:result[1]," +
-         "set:lastIndex,get:exec,call:exec,get:lastIndex,");
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/RegExp/split.js
+++ /dev/null
@@ -1,30 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "Implement RegExp.prototype[@@split].";
-
-print(BUGNUMBER + ": " + summary);
-
-assertEq(RegExp.prototype[Symbol.split].name, "[Symbol.split]");
-assertEq(RegExp.prototype[Symbol.split].length, 2);
-var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, Symbol.split);
-assertEq(desc.configurable, true);
-assertEq(desc.enumerable, false);
-assertEq(desc.writable, true);
-
-var re = /b/;
-var v = re[Symbol.split]("abcAbcABC");
-assertEq(JSON.stringify(v), `["a","cA","cABC"]`);
-
-re = /d/;
-v = re[Symbol.split]("abcAbcABC");
-assertEq(JSON.stringify(v), `["abcAbcABC"]`);
-
-re = /b/ig;
-v = re[Symbol.split]("abcAbcABC");
-assertEq(JSON.stringify(v), `["a","cA","cA","C"]`);
-
-re = /b/ig;
-v = re[Symbol.split]("abcAbcABC", 2);
-assertEq(JSON.stringify(v), `["a","cA"]`);
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
deleted file mode 100644
--- a/js/src/tests/ecma_6/String/split.js
+++ /dev/null
@@ -1,19 +0,0 @@
-var BUGNUMBER = 887016;
-var summary = "Call RegExp.prototype[@@split] from String.prototype.split.";
-
-print(BUGNUMBER + ": " + summary);
-
-var called = 0;
-var myRegExp = {
-  [Symbol.split](S, limit) {
-    assertEq(S, "abcAbcABC");
-    assertEq(limit, 10);
-    called++;
-    return ["X", "Y", "Z"];
-  }
-};
-assertEq("abcAbcABC".split(myRegExp, 10).join(","), "X,Y,Z");
-assertEq(called, 1);
-
-if (typeof reportCompare === "function")
-    reportCompare(true, true);
--- a/js/src/tests/ecma_6/Symbol/well-known.js
+++ b/js/src/tests/ecma_6/Symbol/well-known.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/licenses/publicdomain/ */
 
 var names = [
     "iterator",
     "match",
     "replace",
     "search",
     "species",
-    "split",
     "toPrimitive",
     "unscopables"
 ];
 
 for (var name of names) {
     // Well-known symbols exist.
     assertEq(typeof Symbol[name], "symbol");
 
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -306,26 +306,24 @@
     macro(symbol, symbol, "symbol") \
     /* Well-known atom names must be continuous and ordered, matching \
      * enum JS::SymbolCode in jsapi.h. */ \
     macro(iterator, iterator, "iterator") \
     macro(match, match, "match") \
     macro(replace, replace, "replace") \
     macro(search, search, "search") \
     macro(species, species, "species") \
-    macro(split, split, "split") \
     macro(toPrimitive, toPrimitive, "toPrimitive") \
     macro(unscopables, unscopables, "unscopables") \
     /* Same goes for the descriptions of the well-known symbols. */ \
     macro(Symbol_hasInstance, Symbol_hasInstance, "Symbol.hasInstance") \
     macro(Symbol_isConcatSpreadable, Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") \
     macro(Symbol_iterator, Symbol_iterator, "Symbol.iterator") \
     macro(Symbol_match,    Symbol_match,    "Symbol.match") \
     macro(Symbol_replace,  Symbol_replace,  "Symbol.replace") \
     macro(Symbol_search,   Symbol_search,   "Symbol.search") \
     macro(Symbol_species,  Symbol_species,  "Symbol.species") \
-    macro(Symbol_split,    Symbol_split,    "Symbol.split") \
     macro(Symbol_toPrimitive, Symbol_toPrimitive, "Symbol.toPrimitive") \
     macro(Symbol_unscopables, Symbol_unscopables, "Symbol.unscopables") \
     /* Function names for properties named by symbols. */ \
     macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \
 
 #endif /* vm_CommonPropertyNames_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -480,24 +480,16 @@ GlobalObject::initSelfHostingBuiltins(JS
     RootedValue std_species(cx);
     std_species.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::species));
     if (!JS_DefineProperty(cx, global, "std_species", std_species,
                            JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
-    RootedValue std_split(cx);
-    std_split.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::split));
-    if (!JS_DefineProperty(cx, global, "std_split", std_split,
-                           JSPROP_PERMANENT | JSPROP_READONLY))
-    {
-        return false;
-    }
-
     return InitBareBuiltinCtor(cx, global, JSProto_Array) &&
            InitBareBuiltinCtor(cx, global, JSProto_TypedArray) &&
            InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
            InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
            InitBareWeakMapCtor(cx, global) &&
            InitStopIterationClass(cx, global) &&
            InitSelfHostingCollectionIteratorFunctions(cx, global) &&
            DefineFunctions(cx, global, builtins, AsIntrinsic);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1639,64 +1639,16 @@ intrinsic_RegExpEscapeMetaChars(JSContex
     if (!result)
         return false;
 
     args.rval().setString(result);
     return true;
 }
 
 bool
-js::intrinsic_StringSplitString(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-
-    RootedString string(cx, args[0].toString());
-    RootedString sep(cx, args[1].toString());
-
-    RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
-    if (!group)
-        return false;
-
-    RootedObject aobj(cx);
-    aobj = str_split_string(cx, group, string, sep, INT32_MAX);
-    if (!aobj)
-        return false;
-
-    args.rval().setObject(*aobj);
-    return true;
-}
-
-static bool
-intrinsic_StringSplitStringLimit(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 3);
-
-    RootedString string(cx, args[0].toString());
-    RootedString sep(cx, args[1].toString());
-
-    // args[2] should be already in UInt32 range, but it could be double typed,
-    // because of Ion optimization.
-    uint32_t limit = uint32_t(args[2].toNumber());
-
-    RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array));
-    if (!group)
-        return false;
-
-    RootedObject aobj(cx);
-    aobj = str_split_string(cx, group, string, sep, limit);
-    if (!aobj)
-        return false;
-
-    args.rval().setObject(*aobj);
-    return true;
-}
-
-bool
 CallSelfHostedNonGenericMethod(JSContext* cx, const CallArgs& args)
 {
     // This function is called when a self-hosted method is invoked on a
     // wrapper object, like a CrossCompartmentWrapper. The last argument is
     // the name of the self-hosted function. The other arguments are the
     // arguments to pass to this function.
 
     MOZ_ASSERT(args.length() > 0);
@@ -2271,19 +2223,19 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("std_Reflect_getPrototypeOf",          Reflect_getPrototypeOf,       1,0),
     JS_FN("std_Reflect_isExtensible",            Reflect_isExtensible,         1,0),
 
     JS_FN("std_Set_has",                         SetObject::has,               1,0),
     JS_FN("std_Set_iterator",                    SetObject::values,            0,0),
 
     JS_INLINABLE_FN("std_String_fromCharCode",   str_fromCharCode,             1,0, StringFromCharCode),
     JS_INLINABLE_FN("std_String_charCodeAt",     str_charCodeAt,               1,0, StringCharCodeAt),
-    JS_FN("std_String_includes",                 str_includes,                 1,0),
     JS_FN("std_String_indexOf",                  str_indexOf,                  1,0),
     JS_FN("std_String_lastIndexOf",              str_lastIndexOf,              1,0),
+    JS_INLINABLE_FN("std_String_split",          str_split,                    2,0, StringSplit),
     JS_FN("std_String_startsWith",               str_startsWith,               1,0),
     JS_FN("std_String_toLowerCase",              str_toLowerCase,              0,0),
     JS_FN("std_String_toUpperCase",              str_toUpperCase,              0,0),
 
     JS_FN("std_WeakMap_has",                     WeakMap_has,                  1,0),
     JS_FN("std_WeakMap_get",                     WeakMap_get,                  2,0),
     JS_FN("std_WeakMap_set",                     WeakMap_set,                  2,0),
     JS_FN("std_WeakMap_delete",                  WeakMap_delete,               1,0),
@@ -2540,19 +2492,16 @@ static const JSFunctionSpec intrinsic_fu
                     RegExpInstanceOptimizable),
     JS_FN("RegExpGetSubstitution", intrinsic_RegExpGetSubstitution, 6,0),
     JS_FN("RegExpEscapeMetaChars", intrinsic_RegExpEscapeMetaChars, 1,0),
 
     JS_FN("FlatStringMatch", FlatStringMatch, 2,0),
     JS_FN("FlatStringSearch", FlatStringSearch, 2,0),
     JS_INLINABLE_FN("StringReplaceString", intrinsic_StringReplaceString, 3, 0,
                     IntrinsicStringReplaceString),
-    JS_INLINABLE_FN("StringSplitString", intrinsic_StringSplitString, 2, 0,
-                    IntrinsicStringSplitString),
-    JS_FN("StringSplitStringLimit", intrinsic_StringSplitStringLimit, 3, 0),
 
     // See builtin/RegExp.h for descriptions of the regexp_* functions.
     JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
     JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
     JS_FN("regexp_construct", regexp_construct_self_hosting, 2,0),
 
     JS_FN("IsMatchFlagsArgumentEnabled", IsMatchFlagsArgumentEnabled, 0,0),
     JS_FN("WarnOnceAboutFlagsArgument", WarnOnceAboutFlagsArgument, 0,0),
--- a/js/src/vm/SelfHosting.h
+++ b/js/src/vm/SelfHosting.h
@@ -34,14 +34,11 @@ void
 FillSelfHostingCompileOptions(JS::CompileOptions& options);
 
 bool
 CallSelfHostedFunction(JSContext* cx, char const* name, InvokeArgs& args);
 
 bool
 CallSelfHostedFunction(JSContext* cx, HandlePropertyName name, InvokeArgs& args);
 
-bool
-intrinsic_StringSplitString(JSContext* cx, unsigned argc, JS::Value* vp);
-
 } /* namespace js */
 
 #endif /* vm_SelfHosting_h_ */
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -222,17 +222,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   gPrototypeProperties['Function'] =
     ["constructor", "toSource", "toString", "apply", "call", "bind",
      "isGenerator", "length", "name", "arguments", "caller"];
   gConstructorProperties['Function'] = constructorProps([])
 
   gPrototypeProperties['RegExp'] =
     ["constructor", "toSource", "toString", "compile", "exec", "test",
-     Symbol.match, Symbol.replace, Symbol.search, Symbol.split,
+     Symbol.match, Symbol.replace, Symbol.search,
      "flags", "global", "ignoreCase", "multiline", "source", "sticky", "unicode",
      "lastIndex"];
   gConstructorProperties['RegExp'] =
     constructorProps(["input", "lastMatch", "lastParen",
                       "leftContext", "rightContext", "$1", "$2", "$3", "$4",
                       "$5", "$6", "$7", "$8", "$9", "$_", "$&", "$+",
                       "$`", "$'"])