Bug 887016 - Part 13: Implement RegExp.prototype[@@split] and call it from String.prototype.split. r=h4writer,till
authorTooru Fujisawa <arai_a@mac.com>
Sat, 05 Sep 2015 22:01:43 +0900
changeset 292179 1a3a6133271c6072773e399eac66426ddcd3bfaf
parent 292178 cd13c095d3764559d2eb23d380ef5a72a6fbfc06
child 292180 b4e25cbe3dcbcf4018b59505816de535a0c29a07
push id74764
push userarai_a@mac.com
push dateThu, 07 Apr 2016 10:49:15 +0000
treeherdermozilla-inbound@4d0f975a2311 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer, till
bugs887016
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 887016 - Part 13: Implement RegExp.prototype[@@split] and call it from String.prototype.split. r=h4writer,till
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(std_String_split, locale, "-");
+    var subtags = callFunction(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(std_String_split, extension, "-");
+        extensionSubtags = callFunction(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,16 +644,17 @@ 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,16 +481,194 @@ 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
@@ -275,16 +275,93 @@ 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,16 +25,17 @@
 #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"
@@ -5656,17 +5657,19 @@ GetTemplateObjectForNative(JSContext* cx
                 }
                 res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
                                                             TenuredObject));
                 return !!res;
             }
         }
     }
 
-    if (native == js::str_split && args.length() == 1 && args[0].isString()) {
+    if (native == js::intrinsic_StringSplitString && args.length() == 2 && args[0].isString() &&
+        args[1].isString())
+    {
         ObjectGroup* group = ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array);
         if (!group)
             return false;
         if (group->maybePreliminaryObjects()) {
             *skipAttach = true;
             return true;
         }
 
@@ -5709,29 +5712,29 @@ GetTemplateObjectForClassHook(JSContext*
         templateObject.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr));
         return !!templateObject;
     }
 
     return true;
 }
 
 static bool
-IsOptimizableCallStringSplit(Value callee, Value thisv, int argc, Value* args)
-{
-    if (argc != 1 || !thisv.isString() || !args[0].isString())
+IsOptimizableCallStringSplit(Value callee, int argc, Value* args)
+{
+    if (argc != 2 || !args[0].isString() || !args[1].isString())
         return false;
 
-    if (!thisv.toString()->isAtom() || !args[0].toString()->isAtom())
+    if (!args[0].toString()->isAtom() || !args[1].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::str_split)
+    if (!calleeFun.isNative() || calleeFun.native() != js::intrinsic_StringSplitString)
         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,
@@ -5748,17 +5751,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, thisv, argc, vp + 2))
+    if (stub->numOptimizedStubs() == 0 && IsOptimizableCallStringSplit(callee, 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;
@@ -5996,31 +5999,30 @@ 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, thisv, argc, args))
+    if (!IsOptimizableCallStringSplit(callee, argc, args))
         return true;
 
     MOZ_ASSERT(callee.isObject());
     MOZ_ASSERT(callee.toObject().is<JSFunction>());
 
-    RootedString thisString(cx, thisv.toString());
-    RootedString argString(cx, args[0].toString());
+    RootedString str(cx, args[0].toString());
+    RootedString sep(cx, args[1].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.
@@ -6033,17 +6035,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), thisString, argString,
+                                          script->pcToOffset(pc), str, sep,
                                           arr);
     ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
     stub->addNewStub(newStub);
     *attached = true;
     return true;
@@ -6925,77 +6927,82 @@ 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, Arg0Val, +ICStackValueOffset+ ]
+    // 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);
+
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
     Label failureRestoreArgc;
 #ifdef DEBUG
-    Label oneArg;
+    Label twoArg;
     Register argcReg = R0.scratchReg();
-    masm.branch32(Assembler::Equal, argcReg, Imm32(1), &oneArg);
-    masm.assumeUnreachable("Expected argc == 1");
-    masm.bind(&oneArg);
+    masm.branch32(Assembler::Equal, argcReg, Imm32(2), &twoArg);
+    masm.assumeUnreachable("Expected argc == 2");
+    masm.bind(&twoArg);
 #endif
     Register scratchReg = regs.takeAny();
 
-    // Guard that callee is native function js::str_split.
+    // Guard that callee is native function js::intrinsic_StringSplitString.
     {
-        Address calleeAddr(masm.getStackPointer(), ICStackValueOffset + (2 * sizeof(Value)));
+        Address calleeAddr(masm.getStackPointer(), ICStackValueOffset + CALLEE_DEPTH);
         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 str_split.
+        // Ensure that callee's function impl is the native intrinsic_StringSplitString.
         masm.loadPtr(Address(calleeObj, JSFunction::offsetOfNativeOrScript()), scratchReg);
-        masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(js::str_split), &failureRestoreArgc);
+        masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(js::intrinsic_StringSplitString),
+                       &failureRestoreArgc);
 
         regs.add(calleeVal);
     }
 
-    // Guard argument.
+    // Guard sep.
     {
-        // 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 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 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);
+        // 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);
     }
 
     // Main stub body.
     {
         Register paramReg = regs.takeAny();
 
         // Push arguments.
         enterStubFrame(masm, scratchReg);
@@ -7008,17 +7015,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(1), R0.scratchReg());
+    masm.move32(Imm32(2), 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 expectedThis_;
-    HeapPtrString expectedArg_;
+    HeapPtrString expectedStr_;
+    HeapPtrString expectedSep_;
     HeapPtrObject templateObject_;
 
-    ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* thisString,
-                       JSString* argString, JSObject* templateObject)
+    ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* str,
+                       JSString* sep, JSObject* templateObject)
       : ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub),
-        pcOffset_(pcOffset), expectedThis_(thisString), expectedArg_(argString),
+        pcOffset_(pcOffset), expectedStr_(str), expectedSep_(sep),
         templateObject_(templateObject)
     { }
 
   public:
-    static size_t offsetOfExpectedThis() {
-        return offsetof(ICCall_StringSplit, expectedThis_);
+    static size_t offsetOfExpectedStr() {
+        return offsetof(ICCall_StringSplit, expectedStr_);
     }
 
-    static size_t offsetOfExpectedArg() {
-        return offsetof(ICCall_StringSplit, expectedArg_);
+    static size_t offsetOfExpectedSep() {
+        return offsetof(ICCall_StringSplit, expectedSep_);
     }
 
     static size_t offsetOfTemplateObject() {
         return offsetof(ICCall_StringSplit, templateObject_);
     }
 
-    HeapPtrString& expectedThis() {
-        return expectedThis_;
+    HeapPtrString& expectedStr() {
+        return expectedStr_;
     }
 
-    HeapPtrString& expectedArg() {
-        return expectedArg_;
+    HeapPtrString& expectedSep() {
+        return expectedSep_;
     }
 
     HeapPtrObject& templateObject() {
         return templateObject_;
     }
 
     class Compiler : public ICCallStubCompiler {
       protected:
         ICStub* firstMonitorStub_;
         uint32_t pcOffset_;
-        RootedString expectedThis_;
-        RootedString expectedArg_;
+        RootedString expectedStr_;
+        RootedString expectedSep_;
         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 thisString,
-                 HandleString argString, HandleValue templateObject)
+        Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString str,
+                 HandleString sep, HandleValue templateObject)
           : ICCallStubCompiler(cx, ICStub::Call_StringSplit),
             firstMonitorStub_(firstMonitorStub),
             pcOffset_(pcOffset),
-            expectedThis_(cx, thisString),
-            expectedArg_(cx, argString),
+            expectedStr_(cx, str),
+            expectedSep_(cx, sep),
             templateObject_(cx, &templateObject.toObject())
         { }
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICCall_StringSplit>(space, getStubCode(), firstMonitorStub_, pcOffset_,
-                                               expectedThis_, expectedArg_, templateObject_);
+                                               expectedStr_, expectedSep_, 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** stringOut, JSString** stringArg,
+BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut,
                                                 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;
 
-    *stringOut = stub->toCall_StringSplit()->expectedThis();
-    *stringArg = stub->toCall_StringSplit()->expectedArg();
+    *strOut = stub->toCall_StringSplit()->expectedStr();
+    *sepOut = stub->toCall_StringSplit()->expectedSep();
     *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** stringOut, JSString** stringArg,
+    bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut,
                                       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,22 +7227,23 @@ 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);
+typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t);
 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
@@ -66,22 +66,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,22 +2182,16 @@ 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,16 +15,17 @@
 #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"
 
@@ -184,28 +185,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);
@@ -1462,47 +1463,47 @@ IonBuilder::inlineStringObject(CallInfo&
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineConstantStringSplit(CallInfo& callInfo)
+IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo)
 {
-    if (!callInfo.thisArg()->isConstant())
-        return InliningStatus_NotInlined;
-
     if (!callInfo.getArg(0)->isConstant())
         return InliningStatus_NotInlined;
 
-    MConstant* argval = callInfo.getArg(0)->toConstant();
-    if (argval->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    MConstant* strval = callInfo.thisArg()->toConstant();
+    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();
     if (strval->type() != MIRType_String)
         return InliningStatus_NotInlined;
 
     // Check if exist a template object in stub.
-    JSString* stringThis = nullptr;
-    JSString* stringArg = nullptr;
+    JSString* stringStr = nullptr;
+    JSString* stringSep = nullptr;
     JSObject* templateObject = nullptr;
-    if (!inspector->isOptimizableCallStringSplit(pc, &stringThis, &stringArg, &templateObject))
-        return InliningStatus_NotInlined;
-
-    MOZ_ASSERT(stringThis);
-    MOZ_ASSERT(stringArg);
+    if (!inspector->isOptimizableCallStringSplit(pc, &stringStr, &stringSep, &templateObject))
+        return InliningStatus_NotInlined;
+
+    MOZ_ASSERT(stringStr);
+    MOZ_ASSERT(stringSep);
     MOZ_ASSERT(templateObject);
 
-    if (strval->toString() != stringThis)
-        return InliningStatus_NotInlined;
-
-    if (argval->toString() != stringArg)
+    if (strval->toString() != stringStr)
+        return InliningStatus_NotInlined;
+
+    if (sepval->toString() != stringSep)
         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);
@@ -1565,33 +1566,37 @@ IonBuilder::inlineConstantStringSplit(Ca
     MInstruction* setLength = setInitializedLength(ins, unboxedType, initLength);
     if (!resumeAfter(setLength))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineStringSplit(CallInfo& callInfo)
+IonBuilder::inlineStringSplitString(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
+    if (callInfo.argc() != 2 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    if (callInfo.thisArg()->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-    if (callInfo.getArg(0)->type() != MIRType_String)
-        return InliningStatus_NotInlined;
-
-    IonBuilder::InliningStatus resultConstStringSplit = inlineConstantStringSplit(callInfo);
+    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 (resultConstStringSplit != InliningStatus_NotInlined)
         return resultConstStringSplit;
 
-    JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::str_split);
+    JSObject* templateObject = inspector->getTemplateObjectForNative(pc, js::intrinsic_StringSplitString);
     if (!templateObject)
         return InliningStatus_NotInlined;
 
     TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(templateObject);
     if (retKey->unknownProperties())
         return InliningStatus_NotInlined;
 
     HeapTypeSetKey key = retKey->property(JSID_VOID);
@@ -1603,18 +1608,18 @@ IonBuilder::inlineStringSplit(CallInfo& 
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
     MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject),
                                                   constraints());
     current->add(templateObjectDef);
 
-    MStringSplit* ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
-                                          callInfo.getArg(0), templateObjectDef);
+    MStringSplit* ins = MStringSplit::New(alloc(), constraints(), strArg, sepArg,
+                                          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);
+    JSObject* res = str_split_string(cx, group, str, sep, INT32_MAX);
     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->expectedArg(), "baseline-callstringsplit-arg");
-        TraceEdge(trc, &callStub->expectedThis(), "baseline-callstringsplit-this");
+        TraceEdge(trc, &callStub->expectedSep(), "baseline-callstringsplit-sep");
+        TraceEdge(trc, &callStub->expectedStr(), "baseline-callstringsplit-str");
         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
@@ -4872,16 +4872,17 @@ 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. */
-static bool
-str_includes(JSContext* cx, unsigned argc, Value* vp)
+bool
+js::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,51 +1961,16 @@ 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;
 
@@ -2378,172 +2343,110 @@ 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);
 }
 
-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>
+// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
 static JSObject*
-SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, const Matcher& splitMatch,
-            HandleObjectGroup group, bool unicode)
+SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep,
+            HandleObjectGroup group)
 {
     size_t strLength = str->length();
-    SplitMatchResult result;
-
-    /* Step 11. */
+    size_t sepLength = sep->length();
+    MOZ_ASSERT(sepLength != 0);
+
+    // Step 12.
     if (strLength == 0) {
-        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())
+        // Step 12.a.
+        int match = StringMatch(str, sep, 0);
+
+        // Step 12.b.
+        if (match != -1)
             return NewFullyAllocatedArrayTryUseGroup(cx, group, 0);
 
+        // Steps 12.c-e.
         RootedValue v(cx, StringValue(str));
         return NewCopiedArrayTryUseGroup(cx, group, v.address(), 1);
     }
 
-    /* Step 12. */
-    size_t lastEndIndex = 0;
-    size_t index = 0;
-
-    /* Step 13. */
+    // Step 3 (reordered).
     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())
+    // Step 8 (reordered).
+    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)
             break;
 
-        /* Step 13(c)(i). */
-        size_t sepLength = result.length();
-        size_t endIndex = result.endIndex();
-        if (sepLength == 0 && endIndex == strLength)
-            break;
-
-        /* Step 13(c)(ii). */
+        // Step 14.c.
+        size_t endIndex = match + sepLength;
+
+        // Step 14.c.i.
         if (endIndex == lastEndIndex) {
-            index = AdvanceStringIndex(str, strLength, index, unicode);
+            index++;
             continue;
         }
 
-        /* Step 13(c)(iii). */
+        // Step 14.c.ii.
         MOZ_ASSERT(lastEndIndex < endIndex);
         MOZ_ASSERT(sepLength <= strLength);
         MOZ_ASSERT(lastEndIndex + sepLength <= endIndex);
 
-        /* Steps 13(c)(iii)(1-3). */
+        // Step 14.c.ii.1.
         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 13(c)(iii)(4). */
+        // Step 14.c.ii.5.
         if (splits.length() == limit)
             return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
 
-        /* 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 14.c.ii.6.
+        index = endIndex;
+
+        // Step 14.c.ii.7.
+        lastEndIndex = index;
     }
 
-    /* Steps 14-15. */
+    // Step 15.
     JSString* sub = NewDependentString(cx, str, lastEndIndex, strLength - lastEndIndex);
+
+    // Steps 16-17.
     if (!sub || !splits.append(StringValue(sub)))
         return nullptr;
 
-    /* Step 16. */
+    // Step 18.
     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();
@@ -2562,198 +2465,33 @@ CharSplitHelper(JSContext* cx, HandleLin
         if (!sub)
             return nullptr;
         splits.infallibleAppend(StringValue(sub));
     }
 
     return NewCopiedArrayTryUseGroup(cx, group, splits.begin(), splits.length());
 }
 
-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;
-}
-
+// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.
 JSObject*
-js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep)
+js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit)
+
 {
     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);
 
-    SplitStringMatcher matcher(cx, linearSep);
-    return SplitHelper(cx, linearStr, limit, matcher, group, false);
+    return SplitHelper(cx, linearStr, limit, linearSep, group);
 }
 
 /*
  * Python-esque sequence operations.
  */
 static bool
 str_concat(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2823,17 +2561,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_INLINABLE_FN("split",   str_split,             2,JSFUN_GENERIC_NATIVE, StringSplit),
+    JS_SELF_HOSTED_FN("split",  "String_split",       2,0),
     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),
@@ -2978,16 +2716,17 @@ 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,16 +305,19 @@ 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);
 
@@ -411,21 +414,19 @@ 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);
+str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
+                 uint32_t limit);
 
 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, "ab");
+assertEq(order, "ba");
 assertEqArr(res, ["xyz"]);
 
 assertEqArr("".split(/.?/), []);
 assertEqArr("abc".split(/\b/), ["abc"]);
 
 assertEqArr("abc".split(/((()))./, 2), ["",""]);
 assertEqArr("abc".split(/((((()))))./, 9), ["","","","","","","","",""]);
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/split-this.js
@@ -0,0 +1,12 @@
+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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/split-trace.js
@@ -0,0 +1,229 @@
+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;
+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) {
+  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,");
+
+stopObserve();
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/split.js
@@ -0,0 +1,30 @@
+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);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/String/split.js
@@ -0,0 +1,19 @@
+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,16 +2,17 @@
  * 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
@@ -309,24 +309,26 @@
     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
@@ -482,16 +482,24 @@ 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,16 +1639,64 @@ 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);
@@ -2223,19 +2271,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),
@@ -2492,16 +2540,19 @@ 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,11 +34,14 @@ 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
@@ -214,17 +214,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.match, Symbol.replace, Symbol.search, Symbol.split,
      "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", "$_", "$&", "$+",
                       "$`", "$'", Symbol.species])