Bug 887016 - Part 13: Implement RegExp.prototype[@@split] and call it from String.prototype.split. r=h4writer,till
☠☠ backed out by 5676c7b622c7 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Sat, 05 Sep 2015 22:01:43 +0900
changeset 290631 01da4d30fd114f532009a77acfc663fd54699502
parent 290630 716a5a6539d73d8d3fb70a354333b713beb1acdb
child 290632 7db6a99ec5462372a107b3e346aa8c1ecffaa4ae
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)
reviewersh4writer, till
bugs887016
milestone48.0a1
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
@@ -226,16 +226,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
@@ -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,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"
 
@@ -186,28 +187,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);
@@ -1464,47 +1465,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);
@@ -1567,33 +1568,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);
@@ -1605,18 +1610,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
@@ -4864,16 +4864,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)
 {
@@ -2821,17 +2559,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),
@@ -2976,16 +2714,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,218 @@
+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);
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
@@ -306,24 +306,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
@@ -480,16 +480,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
@@ -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.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", "$_", "$&", "$+",
                       "$`", "$'"])