Bug 1000780 - Part 5: Self-host Function.prototype.bind. r=jandem
authorTill Schneidereit <till@tillschneidereit.net>
Sun, 07 Dec 2014 23:39:23 +0100
changeset 314712 f29f1d9a3cd31eb7a12eaab889a8a138c79d5d98
parent 314711 ccfd55971698728b83ab65acad04c5ab2faab39d
child 314713 44531a21b7f42ece86e0a899fbb863a5e135c3d3
push id5703
push userraliiev@mozilla.com
push dateMon, 07 Mar 2016 14:18:41 +0000
treeherdermozilla-beta@31e373ad5b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1000780
milestone46.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 1000780 - Part 5: Self-host Function.prototype.bind. r=jandem
js/src/builtin/Function.js
js/src/builtin/Intl.js
js/src/builtin/Object.cpp
js/src/builtin/SelfHostingDefines.h
js/src/builtin/TypedObject.cpp
js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
js/src/jit/CodeGenerator.cpp
js/src/jit/InlinableNatives.h
js/src/jit/IonBuilder.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/VMFunctions.cpp
js/src/jscntxtinlines.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsobjinlines.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/moz.build
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/Interpreter.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/Shape.h
js/src/vm/Stack.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Function.js
@@ -0,0 +1,299 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// ES6 spec draft 29, 19.2.3.2
+function FunctionBind(thisArg, ...boundArgs) {
+    // Step 1.
+    var target = this;
+    // Step 2.
+    if (!IsCallable(target))
+        ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, 'Function', 'bind', target);
+
+    // Step 3 (implicit).
+    // Steps 4-5.
+    var F;
+    var argCount = boundArgs.length;
+    switch (argCount) {
+      case 0:
+        F = bind_bindFunction0(target, thisArg, boundArgs);
+        break;
+      case 1:
+        F = bind_bindFunction1(target, thisArg, boundArgs);
+        break;
+      case 2:
+        F = bind_bindFunction2(target, thisArg, boundArgs);
+        break;
+      default:
+        F = bind_bindFunctionN(target, thisArg, boundArgs);
+    }
+
+    // Steps 6-7.
+    var targetHasLength = callFunction(std_Object_hasOwnProperty, target, "length");
+
+    // Step 8.
+    var L;
+    if (targetHasLength) {
+        // Steps 8.a-b.
+        var targetLen = target.length;
+        // Step 8.c.
+        if (typeof targetLen !== 'number') {
+            L = 0;
+        } else {
+            // Steps d.i-ii.
+            L = std_Math_max(0, ToInteger(targetLen) - argCount);
+        }
+    } else {
+        // Step 9.
+        L = 0;
+    }
+
+    // Steps 12-13.
+    var targetName = target.name;
+
+    // Step 14.
+    if (typeof targetName !== "string")
+        targetName = "";
+
+    // Steps 10-11, 15-16.
+    _FinishBoundFunctionInit(F, target, L, targetName);
+
+    // Ensure that the apply intrinsic has been cloned so it can be baked into
+    // JIT code.
+    var funApply = std_Function_apply;
+
+    // Step 17.
+    return F;
+}
+/**
+ * bind_bindFunction{0,1,2} are special cases of the generic bind_bindFunctionN
+ * below. They avoid the need to merge the lists of bound arguments and call
+ * arguments to the bound function into a new list which is then used in a
+ * destructuring call of the bound function.
+ *
+ * All three of these functions again have special-cases for call argument
+ * counts between 0 and 5. For calls with 6+ arguments, all - bound and call -
+ * arguments are copied into an array before invoking the generic call and
+ * construct helper functions. This avoids having to use rest parameters and
+ * destructuring in the fast path.
+ *
+ * All bind_bindFunction{X} functions have the same signature to enable simple
+ * reading out of closed-over state by debugging functions.
+ */
+function bind_bindFunction0(fun, thisArg, boundArgs) {
+    return function bound() {
+        var a = arguments;
+        var constructing = _IsConstructing();
+        if (constructing) {
+            switch (a.length) {
+              case 0:
+                return new fun();
+              case 1:
+                return new fun(a[0]);
+              case 2:
+                return new fun(a[0], a[1]);
+              case 3:
+                return new fun(a[0], a[1], a[2]);
+              case 4:
+                return new fun(a[0], a[1], a[2], a[3]);
+              case 5:
+                return new fun(a[0], a[1], a[2], a[3], a[4]);
+            }
+        } else {
+            switch (a.length) {
+              case 0:
+                return callContentFunction(fun, thisArg);
+              case 1:
+                return callContentFunction(fun, thisArg, a[0]);
+              case 2:
+                return callContentFunction(fun, thisArg, a[0], a[1]);
+              case 3:
+                return callContentFunction(fun, thisArg, a[0], a[1], a[2]);
+              case 4:
+                return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]);
+              case 5:
+                return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]);
+            }
+        }
+        var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
+        return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs);
+    };
+}
+
+function bind_bindFunction1(fun, thisArg, boundArgs) {
+    var bound1 = boundArgs[0];
+    return function bound() {
+        var a = arguments;
+        var constructing = _IsConstructing();
+        if (constructing) {
+            switch (a.length) {
+              case 0:
+                return new fun(bound1);
+              case 1:
+                return new fun(bound1, a[0]);
+              case 2:
+                return new fun(bound1, a[0], a[1]);
+              case 3:
+                return new fun(bound1, a[0], a[1], a[2]);
+              case 4:
+                return new fun(bound1, a[0], a[1], a[2], a[3]);
+              case 5:
+                return new fun(bound1, a[0], a[1], a[2], a[3], a[4]);
+            }
+        } else {
+            switch (a.length) {
+              case 0:
+                return callContentFunction(fun, thisArg, bound1);
+              case 1:
+                return callContentFunction(fun, thisArg, bound1, a[0]);
+              case 2:
+                return callContentFunction(fun, thisArg, bound1, a[0], a[1]);
+              case 3:
+                return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2]);
+              case 4:
+                return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3]);
+              case 5:
+                return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3], a[4]);
+            }
+        }
+        var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
+        return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs);
+    };
+}
+
+function bind_bindFunction2(fun, thisArg, boundArgs) {
+    var bound1 = boundArgs[0];
+    var bound2 = boundArgs[1];
+    return function bound() {
+        var a = arguments;
+        var constructing = _IsConstructing();
+        if (constructing) {
+            switch (a.length) {
+              case 0:
+                return new fun(bound1, bound2);
+              case 1:
+                return new fun(bound1, bound2, a[0]);
+              case 2:
+                return new fun(bound1, bound2, a[0], a[1]);
+              case 3:
+                return new fun(bound1, bound2, a[0], a[1], a[2]);
+              case 4:
+                return new fun(bound1, bound2, a[0], a[1], a[2], a[3]);
+              case 5:
+                return new fun(bound1, bound2, a[0], a[1], a[2], a[3], a[4]);
+            }
+        } else {
+            switch (a.length) {
+              case 0:
+                return callContentFunction(fun, thisArg, bound1, bound2);
+              case 1:
+                return callContentFunction(fun, thisArg, bound1, bound2, a[0]);
+              case 2:
+                return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1]);
+              case 3:
+                return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2]);
+              case 4:
+                return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3]);
+              case 5:
+                return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3], a[4]);
+            }
+        }
+        var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
+        return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs);
+    };
+}
+
+function bind_bindFunctionN(fun, thisArg, boundArgs) {
+    assert(boundArgs.length > 2, "Fast paths should be used for few-bound-args cases.");
+    return function bound() {
+        if (arguments.length === 0) {
+            if (_IsConstructing())
+                return bind_constructFunctionN(fun, boundArgs);
+            else
+                return bind_applyFunctionN(fun, thisArg, boundArgs);
+        }
+        var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
+        return bind_invokeFunctionN(fun, thisArg, _IsConstructing(), boundArgs, callArgs);
+    };
+}
+
+function bind_mapArguments() {
+    var len = arguments.length;
+    var args = std_Array(len);
+    for (var i = 0; i < len; i++)
+        _DefineDataProperty(args, i, arguments[i]);
+    return args;
+}
+
+function bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs) {
+    var boundArgsCount = boundArgs.length;
+    var callArgsCount = callArgs.length;
+    var args = std_Array(boundArgsCount + callArgsCount);
+    for (var i = 0; i < boundArgsCount; i++)
+        _DefineDataProperty(args, i, boundArgs[i]);
+    for (var i = 0; i < callArgsCount; i++)
+        _DefineDataProperty(args, i + boundArgsCount, callArgs[i]);
+    if (constructing)
+        return bind_constructFunctionN(fun, args);
+    return bind_applyFunctionN(fun, thisArg, args);
+}
+
+function bind_applyFunctionN(fun, thisArg, a) {
+    switch (a.length) {
+      case 0:
+        return callContentFunction(fun, thisArg);
+      case 1:
+        return callContentFunction(fun, thisArg, a[0]);
+      case 2:
+        return callContentFunction(fun, thisArg, a[0], a[1]);
+      case 3:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2]);
+      case 4:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]);
+      case 5:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]);
+      case 6:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5]);
+      case 7:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
+      case 8:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+      case 9:
+        return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
+      default:
+        return FUN_APPLY(fun, thisArg, a);
+    }
+}
+
+function bind_constructFunctionN(fun, a) {
+    switch (a.length) {
+      case 1:
+        return new fun(a[0]);
+      case 2:
+        return new fun(a[0], a[1]);
+      case 3:
+        return new fun(a[0], a[1], a[2]);
+      case 4:
+        return new fun(a[0], a[1], a[2], a[3]);
+      case 5:
+        return new fun(a[0], a[1], a[2], a[3], a[4]);
+      case 6:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5]);
+      case 7:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
+      case 8:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+      case 9:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
+      case 10:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
+      case 11:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10]);
+      case 12:
+        return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]);
+      default:
+        assert(a.length !== 0,
+               "bound function construction without args should be handled by caller");
+        return _ConstructFunction(fun, a);
+    }
+}
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1591,17 +1591,17 @@ function Intl_Collator_compare_get() {
     var internals = getCollatorInternals(this, "compare");
 
     // Step 1.
     if (internals.boundCompare === undefined) {
         // Step 1.a.
         var F = collatorCompareToBind;
 
         // Step 1.b-d.
-        var bc = callFunction(std_Function_bind, F, this);
+        var bc = callFunction(FunctionBind, F, this);
         internals.boundCompare = bc;
     }
 
     // Step 2.
     return internals.boundCompare;
 }
 
 
@@ -2031,17 +2031,17 @@ function Intl_NumberFormat_format_get() 
     var internals = getNumberFormatInternals(this, "format");
 
     // Step 1.
     if (internals.boundFormat === undefined) {
         // Step 1.a.
         var F = numberFormatFormatToBind;
 
         // Step 1.b-d.
-        var bf = callFunction(std_Function_bind, F, this);
+        var bf = callFunction(FunctionBind, F, this);
         internals.boundFormat = bf;
     }
     // Step 2.
     return internals.boundFormat;
 }
 
 
 /**
@@ -2727,17 +2727,17 @@ function Intl_DateTimeFormat_format_get(
     var internals = getDateTimeFormatInternals(this, "format");
 
     // Step 1.
     if (internals.boundFormat === undefined) {
         // Step 1.a.
         var F = dateTimeFormatFormatToBind;
 
         // Step 1.b-d.
-        var bf = callFunction(std_Function_bind, F, this);
+        var bf = callFunction(FunctionBind, F, this);
         internals.boundFormat = bf;
     }
 
     // Step 2.
     return internals.boundFormat;
 }
 
 
@@ -2756,17 +2756,17 @@ function Intl_DateTimeFormat_formatToPar
     var internals = getDateTimeFormatInternals(this, "formatToParts");
 
     // Step 1.
     if (internals.boundFormatToParts === undefined) {
         // Step 1.a.
         var F = dateTimeFormatFormatToPartsToBind;
 
         // Step 1.b-d.
-        var bf = callFunction(std_Function_bind, F, this);
+        var bf = callFunction(FunctionBind, F, this);
         internals.boundFormatToParts = bf;
     }
 
     // Step 2.
     return internals.boundFormatToParts;
 }
 
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -1086,24 +1086,29 @@ FinishObjectClassInit(JSContext* cx, JS:
         return false;
     global->setOriginalEval(evalobj);
 
     Rooted<NativeObject*> holder(cx, GlobalObject::getIntrinsicsHolder(cx, global));
     if (!holder)
         return false;
 
     /*
-     * Define self-hosted functions after setting the intrinsics holder
-     * (which is needed to define self-hosted functions)
+     * Define self-hosted functions on Object and Function after setting the
+     * intrinsics holder (which is needed to define self-hosted functions).
      */
     if (!cx->runtime()->isSelfHostingGlobal(global)) {
         if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties))
             return false;
         if (!JS_DefineFunctions(cx, proto, object_methods, OnlyDefineLateProperties))
             return false;
+        RootedObject funProto(cx, global->getOrCreateFunctionPrototype(cx));
+        if (!funProto)
+            return false;
+        if (!JS_DefineFunctions(cx, funProto, function_methods, OnlyDefineLateProperties))
+            return false;
     }
 
     /*
      * The global object should have |Object.prototype| as its [[Prototype]].
      * Eventually we'd like to have standard classes be there from the start,
      * and thus we would know we were always setting what had previously been a
      * null [[Prototype]], but right now some code assumes it can set the
      * [[Prototype]] before standard classes have been initialized.  For now,
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -10,16 +10,20 @@
 #define builtin_SelfHostingDefines_h
 
 // Utility macros.
 #define TO_INT32(x) ((x) | 0)
 #define TO_UINT32(x) ((x) >>> 0)
 #define IS_UINT32(x) ((x) >>> 0 === (x))
 #define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1
 
+// Unforgeable version of Function.prototype.apply.
+#define FUN_APPLY(FUN, RECEIVER, ARGS) \
+  callFunction(std_Function_apply, FUN, RECEIVER, ARGS)
+
 // Unforgeable versions of ARRAY.push(ELEMENT) and ARRAY.slice.
 #define ARRAY_PUSH(ARRAY, ELEMENT) \
   callFunction(std_Array_push, ARRAY, ELEMENT);
 #define ARRAY_SLICE(ARRAY, ELEMENT) \
   callFunction(std_Array_slice, ARRAY, ELEMENT);
 
 // Property descriptor attributes.
 #define ATTR_ENUMERABLE         0x01
@@ -34,16 +38,20 @@
 // stored.
 #define LAZY_FUNCTION_NAME_SLOT 0
 
 // The extended slot which contains a boolean value that indicates whether
 // that the canonical name of the self-hosted builtins is set in self-hosted
 // global. This slot is used only in debug build.
 #define HAS_SELFHOSTED_CANONICAL_NAME_SLOT 0
 
+// Stores the length for bound functions, so the .length property doesn't need
+// to be resolved eagerly.
+#define BOUND_FUN_LENGTH_SLOT 1
+
 // Stores the private WeakMap slot used for WeakSets
 #define WEAKSET_MAP_SLOT 0
 
 #define ITERATOR_SLOT_TARGET 0
 // Used for collection iterators.
 #define ITERATOR_SLOT_RANGE 1
 // Used for list, i.e. Array and String, iterators.
 #define ITERATOR_SLOT_NEXT_INDEX 1
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -226,17 +226,17 @@ const Class js::ScalarTypeDescr::class_ 
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     TypeDescr::finalize,
     ScalarTypeDescr::call
 };
 
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
-    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    {"array", {nullptr, nullptr}, 0, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
     JS_FS_END
 };
 
 int32_t
 ScalarTypeDescr::size(Type t)
 {
     return Scalar::byteSize(t);
--- a/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-object.js
@@ -73,11 +73,11 @@ assertEq(tByteSize([1, 2, 3]),          
 assertEq(tByteSize([1, 2, 3, 4]),                       s(80,  96));
 assertEq(tByteSize([1, 2, 3, 4, 5]),                    s(80,  96));
 assertEq(tByteSize([1, 2, 3, 4, 5, 6]),                 s(80,  96));
 assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7]),              s(112, 128));
 assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7, 8]),           s(112, 128));
 
 // Various forms of functions.
 assertEq(tByteSize(function () {}),                     s(32,  64));
-assertEq(tByteSize(function () {}.bind()),              s(96,  128));
+assertEq(tByteSize(function () {}.bind()),              s(48,  80));
 assertEq(tByteSize(() => 1),                            s(48,  80));
 assertEq(tByteSize(Math.sin),                           s(32,  64));
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1980,17 +1980,17 @@ CodeGenerator::visitLambda(LLambda* lir)
 
     MOZ_ASSERT(!info.singletonType);
 
     masm.createGCObject(output, tempReg, info.fun, gc::DefaultHeap, ool->entry());
 
     emitLambdaInit(output, scopeChain, info);
 
     if (info.flags & JSFunction::EXTENDED) {
-        MOZ_ASSERT(info.fun->allowSuperProperty());
+        MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isSelfHostedBuiltin());
         static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
         masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
         masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
     }
 
     masm.bind(ool->rejoin());
 }
 
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -74,18 +74,16 @@
     _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
     _(StringReplace)                \
                                     \
     _(ObjectCreate)                 \
                                     \
-    _(CallBoundFunction)            \
-                                    \
     _(SimdInt32x4)                  \
     _(SimdFloat32x4)                \
     _(SimdBool32x4)                 \
                                     \
     _(TestBailout)                  \
     _(TestAssertFloat32)            \
     _(TestAssertRecoveredOnBailout) \
                                     \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6265,16 +6265,19 @@ IonBuilder::createThis(JSFunction* targe
         if (!target->isConstructor())
             return nullptr;
 
         MConstant* magic = MConstant::New(alloc(), MagicValue(JS_IS_CONSTRUCTING));
         current->add(magic);
         return magic;
     }
 
+    if (target->isBoundFunction())
+        return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
+
     if (target->isDerivedClassConstructor()) {
         MOZ_ASSERT(target->isClassConstructor());
         return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
     }
 
     // Try baking in the prototype.
     if (MDefinition* createThis = createThisScriptedSingleton(target, callee))
         return createThis;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -196,20 +196,16 @@ IonBuilder::inlineNativeCall(CallInfo& c
         return inlineStrCharAt(callInfo);
       case InlinableNative::StringReplace:
         return inlineStrReplace(callInfo);
 
       // Object natives.
       case InlinableNative::ObjectCreate:
         return inlineObjectCreate(callInfo);
 
-      // Bound function.
-      case InlinableNative::CallBoundFunction:
-        return inlineBoundFunction(callInfo, target);
-
       // SIMD natives.
       case InlinableNative::SimdInt32x4:
         return inlineSimdInt32x4(callInfo, target->native());
       case InlinableNative::SimdFloat32x4:
         return inlineSimdFloat32x4(callInfo, target->native());
       case InlinableNative::SimdBool32x4:
         return inlineSimdBool32x4(callInfo, target->native());
 
@@ -2677,83 +2673,16 @@ IonBuilder::inlineAssertRecoveredOnBailo
 
     current->pop();
     current->push(constant(UndefinedValue()));
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target)
-{
-    trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
-
-    if (!target->getBoundFunctionTarget()->is<JSFunction>())
-        return InliningStatus_NotInlined;
-
-    JSFunction* scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>());
-
-    // Don't optimize if we're constructing and the callee is not a
-    // constructor, so that CallKnown does not have to handle this case
-    // (it should always throw).
-    if (nativeCallInfo.constructing() && !scriptedTarget->isConstructor())
-        return InliningStatus_NotInlined;
-
-    if (nativeCallInfo.constructing() && nativeCallInfo.getNewTarget() != nativeCallInfo.fun())
-        return InliningStatus_NotInlined;
-
-    if (gc::IsInsideNursery(scriptedTarget))
-        return InliningStatus_NotInlined;
-
-    for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
-        const Value val = target->getBoundFunctionArgument(i);
-        if (val.isObject() && gc::IsInsideNursery(&val.toObject()))
-            return InliningStatus_NotInlined;
-        if (val.isString() && !val.toString()->isAtom())
-            return InliningStatus_NotInlined;
-    }
-
-    const Value thisVal = target->getBoundFunctionThis();
-    if (thisVal.isObject() && gc::IsInsideNursery(&thisVal.toObject()))
-        return InliningStatus_NotInlined;
-    if (thisVal.isString() && !thisVal.toString()->isAtom())
-        return InliningStatus_NotInlined;
-
-    size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc();
-    if (argc > ARGS_LENGTH_MAX)
-        return InliningStatus_NotInlined;
-
-    nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked();
-
-    CallInfo callInfo(alloc(), nativeCallInfo.constructing());
-    callInfo.setFun(constant(ObjectValue(*scriptedTarget)));
-    callInfo.setThis(constant(thisVal));
-
-    if (!callInfo.argv().reserve(argc))
-        return InliningStatus_Error;
-
-    for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
-        MConstant* argConst = constant(target->getBoundFunctionArgument(i));
-        callInfo.argv().infallibleAppend(argConst);
-    }
-    for (size_t i = 0; i < nativeCallInfo.argc(); i++)
-        callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i));
-
-    // We only inline when it was not a super-call, so just set the newTarget
-    // to be the target function, per spec.
-    if (nativeCallInfo.constructing())
-        callInfo.setNewTarget(callInfo.fun());
-
-    if (!makeCall(scriptedTarget, callInfo))
-        return InliningStatus_Error;
-
-    return InliningStatus_Inlined;
-}
-
-IonBuilder::InliningStatus
 IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
 {
     if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     // These guards are desirable here and in subsequent atomics to
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -543,22 +543,22 @@ GetIntrinsicValue(JSContext* cx, HandleP
 }
 
 bool
 CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHandleValue rval)
 {
     rval.set(MagicValue(JS_IS_CONSTRUCTING));
 
     if (callee->is<JSFunction>()) {
-        JSFunction* fun = &callee->as<JSFunction>();
+        RootedFunction fun(cx, &callee->as<JSFunction>());
         if (fun->isInterpreted() && fun->isConstructor()) {
             JSScript* script = fun->getOrCreateScript(cx);
             if (!script || !script->ensureHasTypes(cx))
                 return false;
-            if (script->isDerivedClassConstructor()) {
+            if (fun->isBoundFunction() || script->isDerivedClassConstructor()) {
                 rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));
             } else {
                 JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject);
                 if (!thisObj)
                     return false;
                 rval.set(ObjectValue(*thisObj));
             }
         }
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -284,17 +284,16 @@ CallJSNativeConstructor(JSContext* cx, N
      *   have used bind on a proxy function.
      *
      * - new Iterator(x) is user-hookable; it returns x.__iterator__() which
      *   could be any object.
      *
      * - (new Object(Object)) returns the callee.
      */
     MOZ_ASSERT_IF(native != js::proxy_Construct &&
-                  native != js::CallOrConstructBoundFunction &&
                   native != js::IteratorConstructor &&
                   (!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
                   args.rval().isObject() && callee != &args.rval().toObject());
 
     return true;
 }
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -24,16 +24,17 @@
 #include "jsobj.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jswrapper.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
+#include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitFrameIterator.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/Proxy.h"
@@ -88,17 +89,16 @@ IsFunction(HandleValue v)
 {
     return v.isObject() && v.toObject().is<JSFunction>();
 }
 
 static bool
 AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun)
 {
     MOZ_ASSERT(!fun->isBuiltin());
-    MOZ_ASSERT(!fun->isBoundFunction(), "all bound functions are currently native (ergo builtin)");
 
     for (; !iter.done(); ++iter) {
         if (!iter.isFunctionFrame() || iter.isEvalFrame())
             continue;
         if (iter.matchCallee(cx, fun))
             return true;
     }
     return false;
@@ -472,21 +472,28 @@ fun_resolve(JSContext* cx, HandleObject 
         //     assertEq(f.length, 0);  // gets Function.prototype.length!
         //     assertEq(f.name, "");  // gets Function.prototype.name!
         // We use the RESOLVED_LENGTH and RESOLVED_NAME flags as a hack to prevent this
         // bug.
         if (isLength) {
             if (fun->hasResolvedLength())
                 return true;
 
-            uint16_t length;
-            if (!fun->getLength(cx, &length))
-                return false;
+            // Bound functions' length can have values up to MAX_SAFE_INTEGER,
+            // so they're handled differently from other functions.
+            if (fun->isBoundFunction()) {
+                MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
+                v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
+            } else {
+                uint16_t length;
+                if (!fun->getLength(cx, &length))
+                    return false;
 
-            v.setInt32(length);
+                v.setInt32(length);
+            }
         } else {
             if (fun->hasResolvedName())
                 return true;
 
             if (fun->isClassConstructor()) {
                 // It's impossible to have an empty named class expression. We
                 // use empty as a sentinel when creating default class
                 // constructors.
@@ -1282,84 +1289,73 @@ JSFunction::infallibleIsDefaultClassCons
         isDefault = nonLazyScript()->isDefaultClassConstructor();
     }
 
     MOZ_ASSERT_IF(isDefault, isConstructor());
     MOZ_ASSERT_IF(isDefault, isClassConstructor());
     return isDefault;
 }
 
-static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET     = 0;
-static const uint32_t JSSLOT_BOUND_FUNCTION_THIS       = 1;
-static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 2;
-
-static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 3;
-
-inline bool
-JSFunction::initBoundFunction(JSContext* cx, HandleObject target, HandleValue thisArg,
-                              const Value* args, unsigned argslen)
+bool
+JSFunction::getLength(JSContext* cx, uint16_t* length)
 {
-    RootedFunction self(cx, this);
-
-    /*
-     * Convert to a dictionary to set the BOUND_FUNCTION flag and increase
-     * the slot span to cover the arguments and additional slots for the 'this'
-     * value and arguments count.
-     */
-    if (!self->toDictionaryMode(cx))
+    JS::RootedFunction self(cx, this);
+    MOZ_ASSERT(!self->isBoundFunction());
+    if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
         return false;
 
-    if (!self->JSObject::setFlags(cx, BaseShape::BOUND_FUNCTION))
-        return false;
-
-    if (!self->setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
-        return false;
+    *length = self->hasScript() ? self->nonLazyScript()->funLength()
+                                : (self->nargs() - self->hasRest());
+    return true;
+}
 
-    self->setSlot(JSSLOT_BOUND_FUNCTION_TARGET, ObjectValue(*target));
-    self->setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
-    self->setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
-
-    self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
-
-    self->setJitInfo(&jit::JitInfo_CallBoundFunction);
-
-    return true;
+static const js::Value&
+BoundFunctionEnvironmentSlotValue(const JSFunction* fun, uint32_t slotIndex)
+{
+    MOZ_ASSERT(fun->isBoundFunction());
+    MOZ_ASSERT(fun->environment()->is<CallObject>());
+    CallObject* callObject = &fun->environment()->as<CallObject>();
+    return callObject->getSlot(slotIndex);
 }
 
 JSObject*
 JSFunction::getBoundFunctionTarget() const
 {
-    MOZ_ASSERT(isBoundFunction());
-
-    return &getSlot(JSSLOT_BOUND_FUNCTION_TARGET).toObject();
+    js::Value targetVal = BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_TARGET);
+    MOZ_ASSERT(IsCallable(targetVal));
+    return &targetVal.toObject();
 }
 
 const js::Value&
 JSFunction::getBoundFunctionThis() const
 {
-    MOZ_ASSERT(isBoundFunction());
+    return BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_THIS);
+}
 
-    return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
+static ArrayObject*
+GetBoundFunctionArguments(const JSFunction* boundFun)
+{
+    js::Value argsVal = BoundFunctionEnvironmentSlotValue(boundFun, JSSLOT_BOUND_FUNCTION_ARGS);
+    return &argsVal.toObject().as<ArrayObject>();
 }
 
 const js::Value&
-JSFunction::getBoundFunctionArgument(unsigned which) const
+JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const
 {
-    MOZ_ASSERT(isBoundFunction());
     MOZ_ASSERT(which < getBoundFunctionArgumentCount());
 
-    return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which);
+    RootedArrayObject boundArgs(cx, GetBoundFunctionArguments(this));
+    RootedValue res(cx);
+    return boundArgs->getDenseElement(which);
 }
 
 size_t
 JSFunction::getBoundFunctionArgumentCount() const
 {
-    MOZ_ASSERT(isBoundFunction());
-
-    return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
+    return GetBoundFunctionArguments(this)->length();
 }
 
 /* static */ bool
 JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFunction fun)
 {
     MOZ_ASSERT(fun->isInterpretedLazy());
 
     Rooted<LazyScript*> lazy(cx, fun->lazyScriptOrNull());
@@ -1506,242 +1502,54 @@ JSFunction::maybeRelazify(JSRuntime* rt)
     if (comp->isDebuggee() || comp->isSelfHosting)
         return;
 
     // Don't relazify functions with JIT code.
     if (!u.i.s.script_->isRelazifiable())
         return;
 
     // To delazify self-hosted builtins we need the name of the function
-    // to clone. This name is stored in the first extended slot.
-    if (isSelfHostedBuiltin() && !isExtended())
+    // to clone. This name is stored in the first extended slot. Since
+    // that slot is sometimes also used for other purposes, make sure it
+    // contains a string.
+    if (isSelfHostedBuiltin() &&
+        (!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString()))
+    {
         return;
+    }
 
     JSScript* script = nonLazyScript();
 
     flags_ &= ~INTERPRETED;
     flags_ |= INTERPRETED_LAZY;
     LazyScript* lazy = script->maybeLazyScript();
     u.i.s.lazy_ = lazy;
     if (lazy) {
         MOZ_ASSERT(!isSelfHostedBuiltin());
     } else {
         MOZ_ASSERT(isSelfHostedBuiltin());
         MOZ_ASSERT(isExtended());
         MOZ_ASSERT(getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->isAtom());
     }
 }
 
-/* ES5 15.3.4.5.1 and 15.3.4.5.2. */
-bool
-js::CallOrConstructBoundFunction(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedFunction fun(cx, &args.callee().as<JSFunction>());
-    MOZ_ASSERT(fun->isBoundFunction());
-
-    /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
-    unsigned boundArgsLen = fun->getBoundFunctionArgumentCount();
-
-    uint32_t argsLen = args.length();
-    if (argsLen + boundArgsLen > ARGS_LENGTH_MAX) {
-        ReportAllocationOverflow(cx);
-        return false;
-    }
-
-    /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
-    RootedObject target(cx, fun->getBoundFunctionTarget());
-
-    /* 15.3.4.5.1 step 2. */
-    const Value& boundThis = fun->getBoundFunctionThis();
-
-    if (args.isConstructing()) {
-        ConstructArgs cargs(cx);
-        if (!cargs.init(argsLen + boundArgsLen))
-            return false;
-
-        /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
-        for (uint32_t i = 0; i < boundArgsLen; i++)
-            cargs[i].set(fun->getBoundFunctionArgument(i));
-        for (uint32_t i = 0; i < argsLen; i++)
-            cargs[boundArgsLen + i].set(args[i]);
-
-        RootedValue targetv(cx, ObjectValue(*target));
-
-        /* ES6 9.4.1.2 step 5 */
-        RootedValue newTarget(cx);
-        if (&args.newTarget().toObject() == fun)
-            newTarget.set(targetv);
-        else
-            newTarget.set(args.newTarget());
-
-        return Construct(cx, targetv, cargs, newTarget, args.rval());
-    }
-
-    InvokeArgs invokeArgs(cx);
-    if (!invokeArgs.init(argsLen + boundArgsLen))
-        return false;
-
-    /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
-    for (uint32_t i = 0; i < boundArgsLen; i++)
-        invokeArgs[i].set(fun->getBoundFunctionArgument(i));
-    for (uint32_t i = 0; i < argsLen; i++)
-        invokeArgs[boundArgsLen + i].set(args[i]);
-
-    /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
-    invokeArgs.setCallee(ObjectValue(*target));
-    invokeArgs.setThis(boundThis);
-
-    if (!Invoke(cx, invokeArgs))
-        return false;
-
-    args.rval().set(invokeArgs.rval());
-    return true;
-}
-
 static bool
 fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSFunction* fun;
     if (!IsFunctionObject(args.thisv(), &fun)) {
         args.rval().setBoolean(false);
         return true;
     }
 
     args.rval().setBoolean(fun->isGenerator());
     return true;
 }
 
-static JSFunction*
-NewNativeFunctionWithGivenProto(JSContext* cx, Native native, unsigned nargs,
-                                HandleAtom atom, HandleObject proto)
-{
-    return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_FUN, nullptr, atom, proto,
-                                AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto);
-}
-
-static JSFunction*
-NewNativeConstructorWithGivenProto(JSContext* cx, Native native, unsigned nargs,
-                                   HandleAtom atom, HandleObject proto)
-{
-    return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_CTOR, nullptr, atom, proto,
-                                AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto);
-}
-
-// ES6 draft rev32 19.2.3.2
-bool
-js::fun_bind(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Step 1.
-    RootedValue thisv(cx, args.thisv());
-
-    // Step 2.
-    if (!IsCallable(thisv)) {
-        ReportIncompatibleMethod(cx, args, &JSFunction::class_);
-        return false;
-    }
-
-    // Step 3.
-    Value* boundArgs = nullptr;
-    unsigned argslen = 0;
-    if (args.length() > 1) {
-        boundArgs = args.array() + 1;
-        argslen = args.length() - 1;
-    }
-
-    RootedValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue());
-    RootedObject target(cx, &thisv.toObject());
-
-    // This is part of step 4, but we're delaying allocating the function object.
-    RootedObject proto(cx);
-    if (!GetPrototype(cx, target, &proto))
-        return false;
-
-    double length = 0.0;
-    // Try to avoid invoking the resolve hook.
-    if (target->is<JSFunction>() && !target->as<JSFunction>().hasResolvedLength()) {
-        uint16_t len;
-        if (!target->as<JSFunction>().getLength(cx, &len))
-            return false;
-        length = Max(0.0, double(len) - argslen);
-    } else {
-        // Steps 5-6.
-        RootedId id(cx, NameToId(cx->names().length));
-        bool hasLength;
-        if (!HasOwnProperty(cx, target, id, &hasLength))
-            return false;
-
-        // Step 7-8.
-        if (hasLength) {
-            // a-b.
-            RootedValue targetLen(cx);
-            if (!GetProperty(cx, target, target, id, &targetLen))
-                return false;
-            // d.
-            if (targetLen.isNumber())
-                length = Max(0.0, JS::ToInteger(targetLen.toNumber()) - argslen);
-        }
-    }
-
-    RootedString name(cx, cx->names().empty);
-    if (target->is<JSFunction>() && !target->as<JSFunction>().hasResolvedName()) {
-        if (target->as<JSFunction>().atom())
-            name = target->as<JSFunction>().atom();
-    } else {
-        // Steps 11-12.
-        RootedValue targetName(cx);
-        if (!GetProperty(cx, target, target, cx->names().name, &targetName))
-            return false;
-
-        // Step 13.
-        if (targetName.isString())
-            name = targetName.toString();
-    }
-
-    // Step 14. Relevant bits from SetFunctionName.
-    StringBuffer sb(cx);
-    // Disabled for B2G failures.
-    // if (!sb.append("bound ") || !sb.append(name))
-    //   return false;
-    if (!sb.append(name))
-        return false;
-
-    RootedAtom nameAtom(cx, sb.finishAtom());
-    if (!nameAtom)
-        return false;
-
-    // Step 4.
-    RootedFunction fun(cx, target->isConstructor() ?
-      NewNativeConstructorWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto) :
-      NewNativeFunctionWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto));
-    if (!fun)
-        return false;
-
-    if (!fun->initBoundFunction(cx, target, thisArg, boundArgs, argslen))
-        return false;
-
-    // Steps 9-10. Set length again, because NewNativeFunction/NewNativeConstructor
-    // sometimes truncates.
-    if (length != fun->nargs()) {
-        RootedValue lengthVal(cx, NumberValue(length));
-        if (!DefineProperty(cx, fun, cx->names().length, lengthVal, nullptr, nullptr,
-                            JSPROP_READONLY))
-        {
-            return false;
-        }
-    }
-
-    // Step 15.
-    args.rval().setObject(*fun);
-    return true;
-}
-
 /*
  * Report "malformed formal parameter" iff no illegal char or similar scanner
  * error was already reported.
  */
 static bool
 OnBadFormal(JSContext* cx)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
@@ -1750,18 +1558,18 @@ OnBadFormal(JSContext* cx)
 
 const JSFunctionSpec js::function_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,   fun_toSource,   0,0),
 #endif
     JS_FN(js_toString_str,   fun_toString,   0,0),
     JS_FN(js_apply_str,      fun_apply,      2,0),
     JS_FN(js_call_str,       fun_call,       1,0),
-    JS_FN("bind",            fun_bind,       1,0),
     JS_FN("isGenerator",     fun_isGenerator,0,0),
+    JS_SELF_HOSTED_FN("bind", "FunctionBind", 1,JSPROP_DEFINE_LATE),
     JS_FS_END
 };
 
 static bool
 FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind generatorKind)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -23,16 +23,20 @@ class FunctionBox;
 
 class FunctionExtended;
 
 typedef JSNative           Native;
 } // namespace js
 
 struct JSAtomState;
 
+static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET     = 2;
+static const uint32_t JSSLOT_BOUND_FUNCTION_THIS       = 3;
+static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS       = 4;
+
 class JSFunction : public js::NativeObject
 {
   public:
     static const js::Class class_;
 
     enum FunctionKind {
         NormalFunction = 0,
         Arrow,                      /* ES6 '(args) => body' syntax */
@@ -43,17 +47,17 @@ class JSFunction : public js::NativeObje
         AsmJS,                      /* function is an asm.js module or exported function */
         FunctionKindLimit
     };
 
     enum Flags {
         INTERPRETED      = 0x0001,  /* function has a JSScript and environment. */
         CONSTRUCTOR      = 0x0002,  /* function that can be called as a constructor */
         EXTENDED         = 0x0004,  /* structure is FunctionExtended */
-        /* 0x0008 unused */
+        BOUND_FUN        = 0x0008,  /* function was created with Function.prototype.bind. */
         EXPR_BODY        = 0x0010,  /* arrow function with expression body or
                                      * expression closure: function(x) x*x */
         HAS_GUESSED_ATOM = 0x0020,  /* function had no explicit name, but a
                                        name was guessed for it anyway */
         LAMBDA           = 0x0040,  /* function comes from a FunctionExpression, ArrowFunction, or
                                        Function() call (not a FunctionDeclaration or nonstandard
                                        function-statement) */
         SELF_HOSTED      = 0x0080,  /* function is self-hosted builtin and must not be
@@ -181,16 +185,17 @@ class JSFunction : public js::NativeObje
 
     /* Possible attributes of a native function: */
     bool isAsmJSNative()            const { return kind() == AsmJS; }
 
     /* Possible attributes of an interpreted function: */
     bool isExprBody()               const { return flags() & EXPR_BODY; }
     bool hasGuessedAtom()           const { return flags() & HAS_GUESSED_ATOM; }
     bool isLambda()                 const { return flags() & LAMBDA; }
+    bool isBoundFunction()          const { return flags() & BOUND_FUN; }
     bool hasRest()                  const { return flags() & HAS_REST; }
     bool isInterpretedLazy()        const { return flags() & INTERPRETED_LAZY; }
     bool hasScript()                const { return flags() & INTERPRETED; }
     bool isBeingParsed()            const { return flags() & BEING_PARSED; }
 
     bool infallibleIsDefaultClassConstructor(JSContext* cx) const;
 
     // Arrow functions store their lexical new.target in the first extended slot.
@@ -268,16 +273,21 @@ class JSFunction : public js::NativeObje
         this->nargs_ = nargs;
     }
 
     // Can be called multiple times by the parser.
     void setHasRest() {
         flags_ |= HAS_REST;
     }
 
+    void setIsBoundFunction() {
+        MOZ_ASSERT(!isBoundFunction());
+        flags_ |= BOUND_FUN;
+    }
+
     void setIsSelfHostedBuiltin() {
         MOZ_ASSERT(isInterpreted());
         MOZ_ASSERT(!isSelfHostedBuiltin());
         flags_ |= SELF_HOSTED;
         // Self-hosted functions should not be constructable.
         flags_ &= ~CONSTRUCTOR;
     }
     void setIsIntrinsic() {
@@ -438,25 +448,17 @@ class JSFunction : public js::NativeObje
         return !u.i.s.script_;
     }
 
     JSScript* nonLazyScript() const {
         MOZ_ASSERT(!hasUncompiledScript());
         return u.i.s.script_;
     }
 
-    bool getLength(JSContext* cx, uint16_t* length) {
-        JS::RootedFunction self(cx, this);
-        if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
-            return false;
-
-        *length = self->hasScript() ? self->nonLazyScript()->funLength()
-                                    : (self->nargs() - self->hasRest());
-        return true;
-    }
+    inline bool getLength(JSContext* cx, uint16_t* length);
 
     js::LazyScript* lazyScript() const {
         MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
         return u.i.s.lazy_;
     }
 
     js::LazyScript* lazyScriptOrNull() const {
         MOZ_ASSERT(isInterpretedLazy());
@@ -556,22 +558,19 @@ class JSFunction : public js::NativeObje
 
         return offsetof(JSFunction, u.nativeOrScript);
     }
 
     inline void trace(JSTracer* trc);
 
     /* Bound function accessors. */
 
-    inline bool initBoundFunction(JSContext* cx, js::HandleObject target, js::HandleValue thisArg,
-                                  const js::Value* args, unsigned argslen);
-
     JSObject* getBoundFunctionTarget() const;
     const js::Value& getBoundFunctionThis() const;
-    const js::Value& getBoundFunctionArgument(unsigned which) const;
+    const js::Value& getBoundFunctionArgument(JSContext* cx, unsigned which) const;
     size_t getBoundFunctionArgumentCount() const;
 
   private:
     js::HeapPtrScript& mutableScript() {
         MOZ_ASSERT(hasScript());
         return *(js::HeapPtrScript*)&u.i.s.script_;
     }
 
@@ -678,19 +677,16 @@ DefineFunction(JSContext* cx, HandleObje
                gc::AllocKind allocKind = gc::AllocKind::FUNCTION);
 
 bool
 FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
 
 extern bool
 fun_toString(JSContext* cx, unsigned argc, Value* vp);
 
-extern bool
-fun_bind(JSContext* cx, unsigned argc, Value* vp);
-
 /*
  * Function extended with reserved slots for use by various kinds of functions.
  * Most functions do not have these extensions, but enough do that efficient
  * storage is required (no malloc'ed reserved slots).
  */
 class FunctionExtended : public JSFunction
 {
   public:
@@ -814,20 +810,18 @@ ReportIncompatibleMethod(JSContext* cx, 
 
 /*
  * Report an error that call.thisv is not an acceptable this for the callee
  * function.
  */
 extern void
 ReportIncompatible(JSContext* cx, CallReceiver call);
 
-bool
-CallOrConstructBoundFunction(JSContext*, unsigned, js::Value*);
-
 extern const JSFunctionSpec function_methods[];
+extern const JSFunctionSpec function_selfhosted_methods[];
 
 extern bool
 fun_apply(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 fun_call(JSContext* cx, unsigned argc, Value* vp);
 
 } /* namespace js */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -423,17 +423,17 @@ JSObject::nonProxyIsExtensible() const
 
     // [[Extensible]] for ordinary non-proxy objects is an object flag.
     return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
 }
 
 inline bool
 JSObject::isBoundFunction() const
 {
-    return hasAllFlags(js::BaseShape::BOUND_FUNCTION);
+    return is<JSFunction>() && as<JSFunction>().isBoundFunction();
 }
 
 inline bool
 JSObject::watched() const
 {
     return hasAllFlags(js::BaseShape::WATCHED);
 }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3402,32 +3402,38 @@ CloneInnerInterpretedFunction(JSContext*
     RootedObject cloneProto(cx);
     if (srcFun->isStarGenerator()) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
 
     gc::AllocKind allocKind = srcFun->getAllocKind();
-    RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
-                                                  JSFunction::INTERPRETED, nullptr, nullptr,
+    uint16_t flags = srcFun->flags();
+    if (srcFun->isSelfHostedBuiltin()) {
+        // Functions in the self-hosting compartment are only extended in
+        // debug mode. For top-level functions, FUNCTION_EXTENDED gets used by
+        // the cloning algorithm. Do the same for inner functions here.
+        allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+        flags |= JSFunction::Flags::EXTENDED;
+    }
+    RootedAtom atom(cx, srcFun->displayAtom());
+    RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, srcFun->nargs(),
+                                                  JSFunction::Flags(flags), nullptr, atom,
                                                   cloneProto, allocKind, TenuredObject));
     if (!clone)
         return nullptr;
 
     JSScript::AutoDelazify srcScript(cx, srcFun);
     if (!srcScript)
         return nullptr;
     JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript);
     if (!cloneScript)
         return nullptr;
 
-    clone->setArgCount(srcFun->nargs());
-    clone->setFlags(srcFun->flags());
-    clone->initAtom(srcFun->displayAtom());
     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
         return nullptr;
 
     return clone;
 }
 
 bool
 js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -4347,17 +4347,17 @@ js::str_fromCharCode_one_arg(JSContext* 
     rval.setString(str);
     return true;
 }
 
 static const JSFunctionSpec string_static_methods[] = {
     JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
 
     JS_SELF_HOSTED_FN("fromCodePoint",   "String_static_fromCodePoint", 1,0),
-    JS_SELF_HOSTED_FN("raw",             "String_static_raw",           2,0),
+    JS_SELF_HOSTED_FN("raw",             "String_static_raw",           1,0),
     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),
 
     // 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),
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -691,16 +691,17 @@ selfhosted.inputs = [
     'js.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
     'builtin/Classes.js',
     'builtin/Date.js',
     'builtin/Error.js',
+    'builtin/Function.js',
     'builtin/Generator.js',
     'builtin/Intl.js',
     'builtin/IntlData.js',
     'builtin/Iterator.js',
     'builtin/Map.js',
     'builtin/Module.js',
     'builtin/Number.js',
     'builtin/Object.js',
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -7239,17 +7239,17 @@ DebuggerObject_getBoundArguments(JSConte
     }
 
     Rooted<JSFunction*> fun(cx, &refobj->as<JSFunction>());
     size_t length = fun->getBoundFunctionArgumentCount();
     AutoValueVector boundArgs(cx);
     if (!boundArgs.resize(length))
         return false;
     for (size_t i = 0; i < length; i++) {
-        boundArgs[i].set(fun->getBoundFunctionArgument(i));
+        boundArgs[i].set(fun->getBoundFunctionArgument(cx, i));
         if (!dbg->wrapDebuggeeValue(cx, boundArgs[i]))
             return false;
     }
 
     JSObject* aobj = NewDenseCopiedArray(cx, boundArgs.length(), boundArgs.begin());
     if (!aobj)
         return false;
     args.rval().setObject(*aobj);
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -19,16 +19,17 @@
 #include "builtin/Eval.h"
 #if EXPOSE_INTL_API
 # include "builtin/Intl.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/ModuleObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
+#include "builtin/SelfHostingDefines.h"
 #include "builtin/SymbolObject.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakMapObject.h"
 #include "builtin/WeakSetObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/PIC.h"
 #include "vm/RegExpStatics.h"
 #include "vm/RegExpStaticsObject.h"
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -299,17 +299,17 @@ MakeDefaultConstructor(JSContext* cx, JS
     PropertyName* lookup = derived ? cx->names().DefaultDerivedClassConstructor
                                    : cx->names().DefaultBaseClassConstructor;
 
     RootedPropertyName selfHostedName(cx, lookup);
     RootedAtom name(cx, atom == cx->names().empty ? nullptr : atom);
 
     RootedFunction ctor(cx);
     if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name,
-                                                          /* nargs = */ !!derived,
+                                                          /* nargs = */ 0,
                                                           proto, TenuredObject, &ctor))
     {
         return nullptr;
     }
 
     ctor->setIsConstructor();
     ctor->setIsClassConstructor();
 
@@ -341,17 +341,19 @@ js::ValueToCallable(JSContext* cx, Handl
 
 bool
 RunState::maybeCreateThisForConstructor(JSContext* cx)
 {
     if (isInvoke()) {
         InvokeState& invoke = *asInvoke();
         if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
             RootedObject callee(cx, &invoke.args().callee());
-            if (script()->isDerivedClassConstructor()) {
+            if (callee->isBoundFunction()) {
+                invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
+            } else if (script()->isDerivedClassConstructor()) {
                 MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
                 invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
             } else {
                 RootedObject newTarget(cx, &invoke.args().newTarget().toObject());
                 NewObjectKind newKind = invoke.createSingleton() ? SingletonObject : GenericObject;
                 JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind);
                 if (!obj)
                     return false;
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -270,17 +270,17 @@ intrinsic_AssertionFailed(JSContext* cx,
 static bool
 intrinsic_MakeConstructible(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isObject());
     MOZ_ASSERT(args[0].toObject().is<JSFunction>());
     MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin());
-    MOZ_ASSERT(args[1].isObject());
+    MOZ_ASSERT(args[1].isObjectOrNull());
 
     // Normal .prototype properties aren't enumerable.  But for this to clone
     // correctly, it must be enumerable.
     RootedObject ctor(cx, &args[0].toObject());
     if (!DefineProperty(cx, ctor, cx->names().prototype, args[1],
                         nullptr, nullptr,
                         JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT))
     {
@@ -303,16 +303,74 @@ intrinsic_MakeDefaultConstructor(JSConte
 
     ctor->nonLazyScript()->setIsDefaultClassConstructor();
 
     args.rval().setUndefined();
     return true;
 }
 
 /*
+ * Used to mark bound functions as such and make them constructible if the
+ * target is.
+ * Also sets the name and correct length, both of which are more costly to
+ * do in JS.
+ */
+static bool
+intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 4);
+    MOZ_ASSERT(IsCallable(args[1]));
+    MOZ_ASSERT(args[2].isNumber());
+    MOZ_ASSERT(args[3].isString());
+
+    RootedFunction bound(cx, &args[0].toObject().as<JSFunction>());
+    bound->setIsBoundFunction();
+    RootedObject targetObj(cx, &args[1].toObject());
+    MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj);
+    if (targetObj->isConstructor())
+        bound->setIsConstructor();
+
+    // 9.4.1.3 BoundFunctionCreate, steps 2-3,8.
+    RootedObject proto(cx);
+    GetPrototype(cx, targetObj, &proto);
+    if (bound->getProto() != proto) {
+        if (!SetPrototype(cx, bound, proto))
+            return false;
+    }
+
+    bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, args[2]);
+    MOZ_ASSERT(!bound->hasGuessedAtom());
+    RootedAtom name(cx, AtomizeString(cx, args[3].toString()));
+    if (!name)
+        return false;
+    bound->setAtom(name);
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+intrinsic_SetPrototype(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 2);
+    MOZ_ASSERT(args[0].isObject());
+    MOZ_ASSERT(args[1].isObjectOrNull());
+
+    RootedObject obj(cx, &args[0].toObject());
+    RootedObject proto(cx, args[1].toObjectOrNull());
+    if (!SetPrototype(cx, obj, proto))
+        return false;
+
+    args.rval().setUndefined();
+    return true;
+}
+
+/*
  * Used to decompile values in the nearest non-builtin stack frame, falling
  * back to decompiling in the current frame. Helpful for printing higher-order
  * function arguments.
  *
  * The user must supply the argument number of the value in question; it
  * _cannot_ be automatically determined.
  */
 static bool
@@ -1216,16 +1274,36 @@ intrinsic_LocalTZA(JSContext* cx, unsign
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0, "the LocalTZA intrinsic takes no arguments");
 
     args.rval().setDouble(DateTimeInfo::localTZA());
     return true;
 }
 
 static bool
+intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 2);
+    MOZ_ASSERT(args[0].toObject().is<JSFunction>());
+    MOZ_ASSERT(args[1].toObject().is<ArrayObject>());
+
+    RootedArrayObject argsList(cx, &args[1].toObject().as<ArrayObject>());
+    uint32_t len = argsList->length();
+    ConstructArgs constructArgs(cx);
+    if (!constructArgs.init(len))
+        return false;
+    for (uint32_t index = 0; index < len; index++)
+        constructArgs[index].set(argsList->getDenseElement(index));
+
+    return Construct(cx, args[0], constructArgs, args.rval());
+}
+
+
+static bool
 intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
     ScriptFrameIter iter(cx);
     bool isConstructing = iter.isConstructing();
     args.rval().setBoolean(isConstructing);
@@ -1440,17 +1518,16 @@ static const JSFunctionSpec intrinsic_fu
     JS_INLINABLE_FN("std_Array_shift",           array_shift,                  0,0, ArrayShift),
     JS_FN("std_Array_unshift",                   array_unshift,                1,0),
     JS_INLINABLE_FN("std_Array_slice",           array_slice,                  2,0, ArraySlice),
     JS_FN("std_Array_sort",                      array_sort,                   1,0),
 
     JS_FN("std_Date_now",                        date_now,                     0,0),
     JS_FN("std_Date_valueOf",                    date_valueOf,                 0,0),
 
-    JS_FN("std_Function_bind",                   fun_bind,                     1,0),
     JS_FN("std_Function_apply",                  fun_apply,                    2,0),
 
     JS_INLINABLE_FN("std_Math_floor",            math_floor,                   1,0, MathFloor),
     JS_INLINABLE_FN("std_Math_max",              math_max,                     2,0, MathMax),
     JS_INLINABLE_FN("std_Math_min",              math_min,                     2,0, MathMin),
     JS_INLINABLE_FN("std_Math_abs",              math_abs,                     1,0, MathAbs),
     JS_INLINABLE_FN("std_Math_imul",             math_imul,                    2,0, MathImul),
     JS_INLINABLE_FN("std_Math_log2",             math_log2,                    1,0, MathLog2),
@@ -1461,16 +1538,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("std_Number_valueOf",                  num_valueOf,                  0,0),
 
     JS_INLINABLE_FN("std_Object_create",         obj_create,                   2, 0, ObjectCreate),
     JS_FN("std_Object_propertyIsEnumerable",     obj_propertyIsEnumerable,     1,0),
     JS_FN("std_Object_defineProperty",           obj_defineProperty,           3,0),
     JS_FN("std_Object_getOwnPropertyNames",      obj_getOwnPropertyNames,      1,0),
     JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0),
     JS_FN("std_Object_hasOwnProperty",           obj_hasOwnProperty,           1,0),
+    JS_FN("std_Object_setPrototypeOf",           intrinsic_SetPrototype,       2,0),
     JS_FN("std_Object_toString",                 obj_toString,                 0,0),
 
     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),
 
@@ -1507,25 +1585,27 @@ static const JSFunctionSpec intrinsic_fu
     // Helper funtions after this point.
     JS_INLINABLE_FN("ToObject",      intrinsic_ToObject,                1,0, IntrinsicToObject),
     JS_INLINABLE_FN("IsObject",      intrinsic_IsObject,                1,0, IntrinsicIsObject),
     JS_INLINABLE_FN("ToInteger",     intrinsic_ToInteger,               1,0, IntrinsicToInteger),
     JS_INLINABLE_FN("ToString",      intrinsic_ToString,                1,0, IntrinsicToString),
     JS_FN("ToPropertyKey",           intrinsic_ToPropertyKey,           1,0),
     JS_INLINABLE_FN("IsCallable",    intrinsic_IsCallable,              1,0, IntrinsicIsCallable),
     JS_FN("IsConstructor",           intrinsic_IsConstructor,           1,0),
-    JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
+    JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       2,0),
+    JS_FN("_ConstructFunction",      intrinsic_ConstructFunction,       2,0),
     JS_FN("ThrowRangeError",         intrinsic_ThrowRangeError,         4,0),
     JS_FN("ThrowTypeError",          intrinsic_ThrowTypeError,          4,0),
     JS_FN("ThrowSyntaxError",        intrinsic_ThrowSyntaxError,        4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
-    JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       2,0),
+    JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("MakeDefaultConstructor",  intrinsic_MakeDefaultConstructor,  2,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
+    JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 4,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
     JS_FN("LocalTZA",                intrinsic_LocalTZA,                0,0),
 
     JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing,        0,0,
                     IntrinsicIsConstructing),
     JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel,       3,0,
                     IntrinsicSubstringKernel),
     JS_INLINABLE_FN("_DefineDataProperty",              intrinsic_DefineDataProperty,      4,0,
@@ -2138,17 +2218,16 @@ JSRuntime::cloneSelfHostedFunctionScript
                                          HandleFunction targetFun)
 {
     RootedFunction sourceFun(cx, getUnclonedSelfHostedFunction(cx, name));
     if (!sourceFun)
         return false;
     // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
     // aren't any.
     MOZ_ASSERT(!sourceFun->isGenerator());
-    MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
     MOZ_ASSERT(targetFun->isExtended());
     MOZ_ASSERT(targetFun->isInterpretedLazy());
     MOZ_ASSERT(targetFun->isSelfHostedBuiltin());
 
     RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
     if (!sourceScript)
         return false;
 
@@ -2158,16 +2237,30 @@ JSRuntime::cloneSelfHostedFunctionScript
     // global lexical scope on the scope chain is for uniformity and engine
     // invariants.
     MOZ_ASSERT(IsStaticGlobalLexicalScope(sourceScript->enclosingStaticScope()));
     Rooted<ScopeObject*> enclosingScope(cx, &cx->global()->lexicalScope().staticBlock());
     if (!CloneScriptIntoFunction(cx, enclosingScope, targetFun, sourceScript))
         return false;
     MOZ_ASSERT(!targetFun->isInterpretedLazy());
 
+    // ...rest args don't count as formal args, but are included in nargs. We don't,
+    // however, want to introduce a flag "has rest args" in the declaration of
+    // self-hosted functions, so we fix up the flag and the nargs value here.
+    // Since the target function might have been cloned and relazified before,
+    // this only happens if the target function isn't marked as having rest
+    // args.
+    MOZ_ASSERT(sourceFun->nargs() - sourceFun->hasRest() ==
+               targetFun->nargs() - targetFun->hasRest());
+    MOZ_ASSERT_IF(targetFun->hasRest(), sourceFun->hasRest());
+    if (sourceFun->hasRest() && !targetFun->hasRest()) {
+        targetFun->setHasRest();
+        targetFun->setArgCount(sourceFun->nargs());
+    }
+
     // The target function might have been relazified after its flags changed.
     targetFun->setFlags(targetFun->flags() | sourceFun->flags());
     return true;
 }
 
 bool
 JSRuntime::getUnclonedSelfHostedValue(JSContext* cx, HandlePropertyName name,
                                       MutableHandleValue vp)
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -344,17 +344,17 @@ class BaseShape : public gc::TenuredCell
          *
          * If you add a new flag here, please add appropriate code to
          * JSObject::dump to dump it as part of object representation.
          */
 
         DELEGATE            =    0x8,
         NOT_EXTENSIBLE      =   0x10,
         INDEXED             =   0x20,
-        BOUND_FUNCTION      =   0x40,
+        /* (0x40 is unused) */
         HAD_ELEMENTS_ACCESS =   0x80,
         WATCHED             =  0x100,
         ITERATED_SINGLETON  =  0x200,
         NEW_GROUP_UNKNOWN   =  0x400,
         UNCACHEABLE_PROTO   =  0x800,
         IMMUTABLE_PROTOTYPE = 0x1000,
 
         // See JSObject::isQualifiedVarObj().
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -247,17 +247,19 @@ InterpreterFrame::prologue(JSContext* cx
     if (isModuleFrame())
         return probes::EnterScript(cx, script, nullptr, this);
 
     MOZ_ASSERT(isNonEvalFunctionFrame());
     if (fun()->needsCallObject() && !initFunctionScopeObjects(cx))
         return false;
 
     if (isConstructing()) {
-        if (script->isDerivedClassConstructor()) {
+        if (fun()->isBoundFunction()) {
+            thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
+        } else if (script->isDerivedClassConstructor()) {
             MOZ_ASSERT(callee().isClassConstructor());
             thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
         } else if (thisArgument().isPrimitive()) {
             RootedObject callee(cx, &this->callee());
             RootedObject newTarget(cx, &this->newTarget().toObject());
             JSObject* obj = CreateThisForFunction(cx, callee, newTarget,
                                                   createSingleton() ? SingletonObject : GenericObject);
             if (!obj)