Bug 1232022 - Part 2: Implement JSOP_DEBUGCHECKSELFHOSTED, and use it to check self-hosted callFunction()s. (r=till, r=h4writer)
authorEric Faust <efaustbmo@gmail.com>
Wed, 16 Dec 2015 08:14:41 -0800
changeset 276605 cdc3315bff369dfdd6b71bc638cd3e1ed5e6e388
parent 276604 a91d3c32c0ef17f45e27fd88e0115ce9be40920f
child 276606 e2d7e9400d746483a0ee3dabf665e899f244bc1b
push id69220
push userefaustbmo@gmail.com
push dateWed, 16 Dec 2015 16:15:09 +0000
treeherdermozilla-inbound@e2d7e9400d74 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, h4writer
bugs1232022
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 1232022 - Part 2: Implement JSOP_DEBUGCHECKSELFHOSTED, and use it to check self-hosted callFunction()s. (r=till, r=h4writer)
js/src/builtin/Array.js
js/src/builtin/Iterator.js
js/src/builtin/Map.js
js/src/builtin/Object.js
js/src/builtin/Set.js
js/src/builtin/TypedArray.js
js/src/builtin/Utilities.js
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/js.msg
js/src/vm/CommonPropertyNames.h
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/Opcodes.h
js/src/vm/Xdr.h
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -131,17 +131,17 @@ function ArrayEvery(callbackfn/*, thisAr
     var T = arguments.length > 1 ? arguments[1] : void 0;
 
     /* Steps 6-7. */
     /* Steps a (implicit), and d. */
     for (var k = 0; k < len; k++) {
         /* Step b */
         if (k in O) {
             /* Step c. */
-            if (!callFunction(callbackfn, T, O[k], k, O))
+            if (!callContentFunction(callbackfn, T, O[k], k, O))
                 return false;
         }
     }
 
     /* Step 8. */
     return true;
 }
 
@@ -172,17 +172,17 @@ function ArraySome(callbackfn/*, thisArg
     var T = arguments.length > 1 ? arguments[1] : void 0;
 
     /* Steps 6-7. */
     /* Steps a (implicit), and d. */
     for (var k = 0; k < len; k++) {
         /* Step b */
         if (k in O) {
             /* Step c. */
-            if (callFunction(callbackfn, T, O[k], k, O))
+            if (callContentFunction(callbackfn, T, O[k], k, O))
                 return true;
         }
     }
 
     /* Step 8. */
     return false;
 }
 
@@ -213,17 +213,17 @@ function ArrayForEach(callbackfn/*, this
     var T = arguments.length > 1 ? arguments[1] : void 0;
 
     /* Steps 6-7. */
     /* Steps a (implicit), and d. */
     for (var k = 0; k < len; k++) {
         /* Step b */
         if (k in O) {
             /* Step c. */
-            callFunction(callbackfn, T, O[k], k, O);
+            callContentFunction(callbackfn, T, O[k], k, O);
         }
     }
 
     /* Step 8. */
     return void 0;
 }
 
 function ArrayStaticForEach(list, callbackfn/*, thisArg*/) {
@@ -256,17 +256,17 @@ function ArrayMap(callbackfn/*, thisArg*
     var A = std_Array(len);
 
     /* Step 7-8. */
     /* Step a (implicit), and d. */
     for (var k = 0; k < len; k++) {
         /* Step b */
         if (k in O) {
             /* Step c.i-iii. */
-            var mappedValue = callFunction(callbackfn, T, O[k], k, O);
+            var mappedValue = callContentFunction(callbackfn, T, O[k], k, O);
             _DefineDataProperty(A, k, mappedValue);
         }
     }
 
     /* Step 9. */
     return A;
 }
 
@@ -302,17 +302,17 @@ function ArrayFilter(callbackfn/*, thisA
     /* Steps 8-11. */
     /* Steps 11.a (implicit), and 11.e. */
     for (var k = 0, to = 0; k < len; k++) {
         /* Steps 11.b-c. */
         if (k in O) {
             /* Steps 11.c.i-ii. */
             var kValue = O[k];
             /* Steps 11.c.iii-iv. */
-            var selected = callFunction(callbackfn, T, kValue, k, O);
+            var selected = callContentFunction(callbackfn, T, kValue, k, O);
             /* Step 11.c.v. */
             if (selected)
                 _DefineDataProperty(A, to++, kValue);
         }
     }
 
     /* Step 12. */
     return A;
@@ -484,17 +484,17 @@ function ArrayFind(predicate/*, thisArg*
      * imprecision for large values. Example:
      * var obj = { 18014398509481984: true, length: 18014398509481988 };
      * Array.prototype.find.call(obj, () => true);
      */
     for (var k = 0; k < len; k++) {
         /* Steps a-c. */
         var kValue = O[k];
         /* Steps d-f. */
-        if (callFunction(predicate, T, kValue, k, O))
+        if (callContentFunction(predicate, T, kValue, k, O))
             return kValue;
     }
 
     /* Step 10. */
     return undefined;
 }
 
 /* ES6 draft 2013-05-14 15.4.3.23. */
@@ -518,17 +518,17 @@ function ArrayFindIndex(predicate/*, thi
     /* Steps a (implicit), and g. */
     /* Note: this will hang in some corner-case situations, because of IEEE-754 numbers'
      * imprecision for large values. Example:
      * var obj = { 18014398509481984: true, length: 18014398509481988 };
      * Array.prototype.find.call(obj, () => true);
      */
     for (var k = 0; k < len; k++) {
         /* Steps a-f. */
-        if (callFunction(predicate, T, O[k], k, O))
+        if (callContentFunction(predicate, T, O[k], k, O))
             return k;
     }
 
     /* Step 10. */
     return -1;
 }
 
 /* ES6 draft 2013-09-27 22.1.3.3. */
@@ -766,31 +766,31 @@ function ArrayFrom(items, mapfn=undefine
         // Step 6.f.
         var k = 0;
 
         // Step 6.g.
         // These steps cannot be implemented using a for-of loop.
         // See <https://bugs.ecmascript.org/show_bug.cgi?id=2883>.
         while (true) {
             // Steps 6.g.i-iii.
-            var next = callFunction(iterator.next, iterator);
+            var next = callContentFunction(iterator.next, iterator);
             if (!IsObject(next))
                 ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
 
             // Step 6.g.iv.
             if (next.done) {
                 A.length = k;
                 return A;
             }
 
             // Steps 6.g.v-vi.
             var nextValue = next.value;
 
             // Steps 6.g.vii-viii.
-            var mappedValue = mapping ? callFunction(mapfn, thisArg, nextValue, k) : nextValue;
+            var mappedValue = mapping ? callContentFunction(mapfn, thisArg, nextValue, k) : nextValue;
 
             // Steps 6.g.ix-xi.
             _DefineDataProperty(A, k++, mappedValue);
         }
     }
 
     // Step 7.
     assert(usingIterator === undefined, "`items` can't be an Iterable after step 6.g.iv");
@@ -805,17 +805,17 @@ function ArrayFrom(items, mapfn=undefine
     var A = IsConstructor(C) ? new C(len) : std_Array(len);
 
     // Steps 15-16.
     for (var k = 0; k < len; k++) {
         // Steps 16.a-c.
         var kValue = items[k];
 
         // Steps 16.d-e.
-        var mappedValue = mapping ? callFunction(mapfn, thisArg, kValue, k) : kValue;
+        var mappedValue = mapping ? callContentFunction(mapfn, thisArg, kValue, k) : kValue;
 
         // Steps 16.f-g.
         _DefineDataProperty(A, k, mappedValue);
     }
 
     // Steps 17-18.
     A.length = len;
 
@@ -829,10 +829,10 @@ function ArrayToString() {
     var array = ToObject(this);
 
     // Steps 3-4.
     var func = array.join;
 
     // Steps 5-6.
     if (!IsCallable(func))
         return callFunction(std_Object_toString, array);
-    return callFunction(func, array);
+    return callContentFunction(func, array);
 }
--- a/js/src/builtin/Iterator.js
+++ b/js/src/builtin/Iterator.js
@@ -6,28 +6,28 @@ function IteratorIdentity() {
     return this;
 }
 
 var LegacyIteratorWrapperMap = new std_WeakMap();
 
 function LegacyIteratorNext(arg) {
     var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
     try {
-        return { value: callFunction(iter.next, iter, arg), done: false };
+        return { value: callContentFunction(iter.next, iter, arg), done: false };
     } catch (e) {
         if (e instanceof std_StopIteration)
             return { value: undefined, done: true };
         throw e;
     }
 }
 
 function LegacyIteratorThrow(exn) {
     var iter = callFunction(std_WeakMap_get, LegacyIteratorWrapperMap, this);
     try {
-        return { value: callFunction(iter.throw, iter, exn), done: false };
+        return { value: callContentFunction(iter.throw, iter, exn), done: false };
     } catch (e) {
         if (e instanceof std_StopIteration)
             return { value: undefined, done: true };
         throw e;
     }
 }
 
 function LegacyIterator(iter) {
--- a/js/src/builtin/Map.js
+++ b/js/src/builtin/Map.js
@@ -24,17 +24,17 @@ function MapForEach(callbackfn, thisArg 
 
     /* Step 6-8. */
     var entries = callFunction(std_Map_iterator, M);
     while (true) {
         var result = callFunction(std_Map_iterator_next, entries);
         if (result.done)
             break;
         var entry = result.value;
-        callFunction(callbackfn, thisArg, entry[1], entry[0], M);
+        callContentFunction(callbackfn, thisArg, entry[1], entry[0], M);
     }
 }
 
 var iteratorTemp = { mapIterationResultPair : null };
 
 function MapIteratorNext() {
     // Step 1.
     var O = this;
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -52,17 +52,17 @@ function ObjectIsExtensible(obj) {
 }
 
 /* ES2015 19.1.3.5 Object.prototype.toLocaleString */
 function Object_toLocaleString() {
     // Step 1.
     var O = this;
 
     // Step 2.
-    return callFunction(O.toString, O);
+    return callContentFunction(O.toString, O);
 }
 
 function ObjectDefineSetter(name, setter) {
     var object;
     if (this === null || this === undefined)
         object = global;
     else
         object = ToObject(this);
--- a/js/src/builtin/Set.js
+++ b/js/src/builtin/Set.js
@@ -24,17 +24,17 @@ function SetForEach(callbackfn, thisArg 
 
     /* Step 7-8. */
     var values = callFunction(std_Set_iterator, S);
     while (true) {
         var result = callFunction(std_Set_iterator_next, values);
         if (result.done)
             break;
         var value = result.value;
-        callFunction(callbackfn, thisArg, value, value, S);
+        callContentFunction(callbackfn, thisArg, value, value, S);
     }
 }
 
 // ES6 final draft 23.2.2.2.
 function SetSpecies() {
     // Step 1.
     return this;
 }
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -116,17 +116,17 @@ function TypedArrayEvery(callbackfn, thi
 
     // Steps 8-9.
     // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
     for (var k = 0; k < len; k++) {
         // Steps 9.d.i-9.d.ii.
         var kValue = O[k];
 
         // Steps 9.d.iii-9.d.iv.
-        var testResult = callFunction(callbackfn, T, kValue, k, O);
+        var testResult = callContentFunction(callbackfn, T, kValue, k, O);
 
         // Step 9.d.v.
         if (!testResult)
             return false;
     }
 
     // Step 10.
     return true;
@@ -207,17 +207,17 @@ function TypedArrayFilter(callbackfn, th
     // Step 12.
     var captured = 0;
 
     // Steps 11, 13 and 13.g.
     for (var k = 0; k < len; k++) {
         // Steps 13.b-c.
         var kValue = O[k];
         // Steps 13.d-e.
-        var selected = ToBoolean(callFunction(callbackfn, T, kValue, k, O));
+        var selected = ToBoolean(callContentFunction(callbackfn, T, kValue, k, O));
         // Step 13.f.
         if (selected) {
             // Step 13.f.i.
             callFunction(std_Array_push, kept, kValue);
             // Step 13.f.ii.
             captured++;
         }
     }
@@ -259,17 +259,17 @@ function TypedArrayFind(predicate, thisA
     var T = thisArg;
 
     // Steps 8-9.
     // Steps a (implicit), and g.
     for (var k = 0; k < len; k++) {
         // Steps a-c.
         var kValue = O[k];
         // Steps d-f.
-        if (callFunction(predicate, T, kValue, k, O))
+        if (callContentFunction(predicate, T, kValue, k, O))
             return kValue;
     }
 
     // Step 10.
     return undefined;
 }
 
 // ES6 draft rev28 (2014/10/14) 22.2.3.11 %TypedArray%.prototype.findIndex(predicate[, thisArg]).
@@ -294,53 +294,53 @@ function TypedArrayFindIndex(predicate, 
 
     // Step 7.
     var T = thisArg;
 
     // Steps 8-9.
     // Steps a (implicit), and g.
     for (var k = 0; k < len; k++) {
         // Steps a-f.
-        if (callFunction(predicate, T, O[k], k, O))
+        if (callContentFunction(predicate, T, O[k], k, O))
             return k;
     }
 
     // Step 10.
     return -1;
 }
 
 // ES6 draft rev31 (2015-01-15) 22.1.3.10 %TypedArray%.prototype.forEach(callbackfn[,thisArg])
 function TypedArrayForEach(callbackfn, thisArg = undefined) {
     // This function is not generic.
     if (!IsObject(this) || !IsTypedArray(this)) {
-	return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
-			    "TypedArrayForEach");
+        return callFunction(CallTypedArrayMethodIfWrapped, this, callbackfn, thisArg,
+                            "TypedArrayForEach");
     }
 
     // Step 1-2.
     var O = this;
 
     // Step 3-4.
     var len = TypedArrayLength(O);
 
     // Step 5.
     if (arguments.length === 0)
-	ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
     if (!IsCallable(callbackfn))
-	ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+        ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 6.
     var T = thisArg;
 
     // Step 7-8.
     // Step 7, 8a (implicit) and 8e.
     for (var k = 0; k < len; k++) {
-	// Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
-	// Step 8d.
-	callFunction(callbackfn, T, O[k], k, O);
+        // Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
+        // Step 8d.
+        callContentFunction(callbackfn, T, O[k], k, O);
     }
 
     // Step 9.
     return undefined;
 }
 
 // ES6 draft rev29 (2014/12/06) 22.2.3.13 %TypedArray%.prototype.indexOf(searchElement[, fromIndex]).
 function TypedArrayIndexOf(searchElement, fromIndex = 0) {
@@ -521,17 +521,17 @@ function TypedArrayMap(callbackfn, thisA
     var C = SpeciesConstructor(O, defaultConstructor);
 
     // Steps 10-11.
     var A = new C(len);
 
     // Steps 12, 13.a (implicit) and 13.h.
     for (var k = 0; k < len; k++) {
         // Steps 13.d-e.
-        var mappedValue = callFunction(callbackfn, T, O[k], k, O);
+        var mappedValue = callContentFunction(callbackfn, T, O[k], k, O);
         // Steps 13.f-g.
         A[k] = mappedValue;
     }
 
     // Step 14.
     return A;
 }
 
@@ -562,17 +562,17 @@ function TypedArrayReduce(callbackfn/*, 
 
     // Steps 9-10.
     // Omit some steps, since 'accumulator' should always be O[0] in step 10 for typed arrays.
     var accumulator = arguments.length > 1 ? arguments[1] : O[k++];
 
     // Step 11.
     // Omit steps 11.b-11.c and the 'if' clause in step 11.d, since there are no holes in typed arrays.
     for (; k < len; k++) {
-        accumulator = callFunction(callbackfn, undefined, accumulator, O[k], k, O);
+        accumulator = callContentFunction(callbackfn, undefined, accumulator, O[k], k, O);
     }
 
     // Step 12.
     return accumulator;
 }
 
 // ES6 draft rev30 (2014/12/24) 22.2.3.20 %TypedArray%.prototype.reduceRight(callbackfn[, initialValue]).
 function TypedArrayReduceRight(callbackfn/*, initialValue*/) {
@@ -601,17 +601,17 @@ function TypedArrayReduceRight(callbackf
 
     // Steps 9-10.
     // Omit some steps, since 'accumulator' should always be O[len-1] in step 10 for typed arrays.
     var accumulator = arguments.length > 1 ? arguments[1] : O[k--];
 
     // Step 11.
     // Omit steps 11.b-11.c and the 'if' clause in step 11.d, since there are no holes in typed arrays.
     for (; k >= 0; k--) {
-        accumulator = callFunction(callbackfn, undefined, accumulator, O[k], k, O);
+        accumulator = callContentFunction(callbackfn, undefined, accumulator, O[k], k, O);
     }
 
     // Step 12.
     return accumulator;
 }
 
 // ES6 draft rev29 (2014/12/06) 22.2.3.21 %TypedArray%.prototype.reverse().
 function TypedArrayReverse() {
@@ -710,25 +710,25 @@ function SetFromNonTypedArray(target, ar
 
     // Steps 12-15, 21, 23-24.
     while (targetOffset < limitOffset) {
         // Steps 24a-c.
         var kNumber = ToNumber(src[k]);
 
         // Step 24d.  This explicit check will be unnecessary when we implement
         // throw-on-getting/setting-element-in-detached-buffer semantics.
-	if (!isShared) {
+        if (!isShared) {
             if (targetBuffer === null) {
-		// A typed array previously using inline storage may acquire a
-		// buffer, so we must check with the source.
-		targetBuffer = ViewedArrayBufferIfReified(target);
+                // A typed array previously using inline storage may acquire a
+                // buffer, so we must check with the source.
+                targetBuffer = ViewedArrayBufferIfReified(target);
             }
             if (IsDetachedBuffer(targetBuffer))
-		ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
-	}
+                ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
+        }
 
         // Step 24e.
         target[targetOffset] = kNumber;
 
         // Steps 24f-g.
         k++;
         targetOffset++;
     }
@@ -887,17 +887,17 @@ function TypedArraySome(callbackfn, this
 
     // Steps 8-9.
     // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
     for (var k = 0; k < len; k++) {
         // Steps 9.d.i-9.d.ii.
         var kValue = O[k];
 
         // Steps 9.d.iii-9.d.iv.
-        var testResult = callFunction(callbackfn, T, kValue, k, O);
+        var testResult = callContentFunction(callbackfn, T, kValue, k, O);
 
         // Step 9.d.v.
         if (testResult)
             return true;
     }
 
     // Step 10.
     return false;
@@ -1069,17 +1069,17 @@ function TypedArrayFrom(constructor, tar
         var iterator = GetIterator(items, usingIterator);
 
         // Step 10.c.
         var values = new List();
 
         // Steps 10.d-e.
         while (true) {
             // Steps 10.e.i-ii.
-            var next = callFunction(iterator.next, iterator);
+            var next = callContentFunction(iterator.next, iterator);
             if (!IsObject(next))
                 ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
 
             // Steps 10.e.iii-vi.
             if (next.done)
                 break;
             callFunction(std_Array_push, values, next.value);
         }
@@ -1093,17 +1093,17 @@ function TypedArrayFrom(constructor, tar
         var targetObj = new C(len);
 
         // Steps 10.i-j.
         for (var k = 0; k < len; k++) {
             // Steps 10.j.i-ii.
             var kValue = values[k];
 
             // Steps 10.j.iii-iv.
-            var mappedValue = mapping ? callFunction(mapfn, T, kValue, k) : kValue;
+            var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue;
 
             // Steps 10.j.v-vi.
             targetObj[k] = mappedValue;
         }
 
         // Step 10.k.
         // asserting that `values` is empty here would require removing them one by one from
         // the list's start in the loop above. That would introduce unacceptable overhead.
@@ -1127,17 +1127,17 @@ function TypedArrayFrom(constructor, tar
     var targetObj = new C(len);
 
     // Steps 19-20.
     for (var k = 0; k < len; k++) {
         // Steps 20.a-c.
         var kValue = arrayLike[k];
 
         // Steps 20.d-e.
-        var mappedValue = mapping ? callFunction(mapfn, T, kValue, k) : kValue;
+        var mappedValue = mapping ? callContentFunction(mapfn, T, kValue, k) : kValue;
 
         // Steps 20.f-g.
         targetObj[k] = mappedValue;
     }
 
     // Step 21.
     return targetObj;
 }
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -137,17 +137,17 @@ function IsPropertyKey(argument) {
 
 /* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.4.1 */
 function GetIterator(obj, method) {
     // Steps 1-2.
     if (arguments.length === 1)
         method = GetMethod(obj, std_iterator);
 
     // Steps 3-4.
-    var iterator = callFunction(method, obj);
+    var iterator = callContentFunction(method, obj);
 
     // Step 5.
     if (!IsObject(iterator))
         ThrowTypeError(JSMSG_NOT_ITERABLE, ToString(iterator));
 
     // Step 6.
     return iterator;
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7070,16 +7070,23 @@ BytecodeEmitter::emitSelfHostedCallFunct
         return false;
     }
 
     ParseNode* pn2 = pn->pn_head;
     ParseNode* funNode = pn2->pn_next;
     if (!emitTree(funNode))
         return false;
 
+#ifdef DEBUG
+    if (pn2->name() != cx->names().callContentFunction) {
+        if (!emit1(JSOP_DEBUGCHECKSELFHOSTED))
+            return false;
+    }
+#endif
+
     ParseNode* thisArg = funNode->pn_next;
     if (!emitTree(thisArg))
         return false;
 
     bool oldEmittingForInit = emittingForInit;
     emittingForInit = false;
 
     for (ParseNode* argpn = thisArg->pn_next; argpn; argpn = argpn->pn_next) {
@@ -7170,18 +7177,21 @@ BytecodeEmitter::emitCallOrNew(ParseNode
     switch (pn2->getKind()) {
       case PNK_NAME:
         if (emitterMode == BytecodeEmitter::SelfHosting && !spread) {
             // We shouldn't see foo(bar) = x in self-hosted code.
             MOZ_ASSERT(!(pn->pn_xflags & PNX_SETCALL));
 
             // Calls to "forceInterpreter", "callFunction" or "resumeGenerator"
             // in self-hosted code generate inline bytecode.
-            if (pn2->name() == cx->names().callFunction)
+            if (pn2->name() == cx->names().callFunction ||
+                pn2->name() == cx->names().callContentFunction)
+            {
                 return emitSelfHostedCallFunction(pn);
+            }
             if (pn2->name() == cx->names().resumeGenerator)
                 return emitSelfHostedResumeGenerator(pn);
             if (pn2->name() == cx->names().forceInterpreter)
                 return emitSelfHostedForceInterpreter(pn);
             // Fall through.
         }
         if (!emitNameOp(pn2, callop))
             return false;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -4236,8 +4236,27 @@ BaselineCompiler::emit_JSOP_RESUME()
     // return value and we're done.
     masm.bind(&returnTarget);
     masm.computeEffectiveAddress(frame.addressOfStackValue(frame.peek(-1)), masm.getStackPointer());
     frame.popn(2);
     frame.push(R0);
     return true;
 }
 
+typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
+static const VMFunction CheckSelfHostedInfo = FunctionInfo<CheckSelfHostedFn>(js::Debug_CheckSelfHosted);
+
+bool
+BaselineCompiler::emit_JSOP_DEBUGCHECKSELFHOSTED()
+{
+#ifdef DEBUG
+    frame.syncStack(0);
+
+    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+    prepareVMCall();
+    pushArg(R0);
+    if (!callVM(CheckSelfHostedInfo))
+        return false;
+#endif
+    return true;
+
+}
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -214,17 +214,18 @@ namespace jit {
     _(JSOP_SPREADSUPERCALL)    \
     _(JSOP_THROWSETCONST)      \
     _(JSOP_THROWSETALIASEDCONST) \
     _(JSOP_INITHIDDENPROP_GETTER) \
     _(JSOP_INITHIDDENPROP_SETTER) \
     _(JSOP_INITHIDDENELEM)     \
     _(JSOP_INITHIDDENELEM_GETTER) \
     _(JSOP_INITHIDDENELEM_SETTER) \
-    _(JSOP_CHECKOBJCOERCIBLE)
+    _(JSOP_CHECKOBJCOERCIBLE)  \
+    _(JSOP_DEBUGCHECKSELFHOSTED)
 
 class BaselineCompiler : public BaselineCompilerSpecific
 {
     FixedList<Label>            labels_;
     NonAssertingLabel           return_;
     NonAssertingLabel           postBarrierSlot_;
 
     // Native code offset right before the scope chain is initialized.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10369,16 +10369,27 @@ CodeGenerator::visitCheckObjCoercible(LC
     masm.branchTestNull(Assembler::Equal, checkValue, &fail);
     masm.branchTestUndefined(Assembler::NotEqual, checkValue, &done);
     masm.bind(&fail);
     pushArg(checkValue);
     callVM(ThrowObjectCoercibleInfo, ins);
     masm.bind(&done);
 }
 
+typedef bool (*CheckSelfHostedFn)(JSContext*, HandleValue);
+static const VMFunction CheckSelfHostedInfo = FunctionInfo<CheckSelfHostedFn>(js::Debug_CheckSelfHosted);
+
+void
+CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins)
+{
+    ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::CheckValue);
+    pushArg(checkValue);
+    callVM(CheckSelfHostedInfo, ins);
+}
+
 void
 CodeGenerator::visitRandom(LRandom* ins)
 {
     using mozilla::non_crypto::XorShift128PlusRNG;
 
     FloatRegister output = ToFloatRegister(ins->output());
     Register tempReg = ToRegister(ins->temp0());
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -340,16 +340,17 @@ class CodeGenerator : public CodeGenerat
     void visitLexicalCheck(LLexicalCheck* ins);
     void visitThrowRuntimeLexicalError(LThrowRuntimeLexicalError* ins);
     void visitGlobalNameConflictsCheck(LGlobalNameConflictsCheck* ins);
     void visitDebugger(LDebugger* ins);
     void visitNewTarget(LNewTarget* ins);
     void visitArrowNewTarget(LArrowNewTarget* ins);
     void visitCheckReturn(LCheckReturn* ins);
     void visitCheckObjCoercible(LCheckObjCoercible* ins);
+    void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
 
     void visitCheckOverRecursed(LCheckOverRecursed* lir);
     void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
 
     void visitInterruptCheckImplicit(LInterruptCheckImplicit* ins);
     void visitOutOfLineInterruptCheckImplicit(OutOfLineInterruptCheckImplicit* ins);
 
     void visitUnboxFloatingPoint(LUnboxFloatingPoint* lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2094,16 +2094,29 @@ IonBuilder::inspectOpcode(JSOp op)
         break;
 
       case JSOP_NEWTARGET:
         return jsop_newtarget();
 
       case JSOP_CHECKOBJCOERCIBLE:
         return jsop_checkobjcoercible();
 
+      case JSOP_DEBUGCHECKSELFHOSTED:
+      {
+#ifdef DEBUG
+        MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
+        current->add(check);
+        current->push(check);
+        if (!resumeAfter(check))
+            return false;
+#endif
+        return true;
+      }
+
+
 #ifdef DEBUG
       case JSOP_PUSHBLOCKSCOPE:
       case JSOP_FRESHENBLOCKSCOPE:
       case JSOP_POPBLOCKSCOPE:
         // These opcodes are currently unhandled by Ion, but in principle
         // there's no reason they couldn't be.  Whenever this happens, OSR will
         // have to consider that JSOP_FRESHENBLOCK mutates the scope chain --
         // right now it caches the scope chain in MBasicBlock::scopeChain().
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4328,16 +4328,29 @@ LIRGenerator::visitCheckObjCoercible(MCh
 
     LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible();
     useBoxAtStart(lir, LCheckObjCoercible::CheckValue, checkVal);
     redefine(ins, checkVal);
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
+void
+LIRGenerator::visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins)
+{
+    MDefinition* checkVal = ins->checkValue();
+    MOZ_ASSERT(checkVal->type() == MIRType_Value);
+
+    LDebugCheckSelfHosted* lir = new (alloc()) LDebugCheckSelfHosted();
+    useBoxAtStart(lir, LDebugCheckSelfHosted::CheckValue, checkVal);
+    redefine(ins, checkVal);
+    add(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
 static void
 SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint)
 {
     Fprinter& out = JitSpewPrinter();
     out.printf("Current resume point %p details:\n", (void*)resumePoint);
     out.printf("    frame count: %u\n", resumePoint->frameCount());
 
     if (ins) {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -305,14 +305,15 @@ class LIRGenerator : public LIRGenerator
     void visitGlobalNameConflictsCheck(MGlobalNameConflictsCheck* ins);
     void visitDebugger(MDebugger* ins);
     void visitNewTarget(MNewTarget* ins);
     void visitArrowNewTarget(MArrowNewTarget* ins);
     void visitAtomicIsLockFree(MAtomicIsLockFree* ins);
     void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
     void visitCheckReturn(MCheckReturn* ins);
     void visitCheckObjCoercible(MCheckObjCoercible* ins);
+    void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Lowering_h */
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13351,16 +13351,39 @@ class MCheckObjCoercible
         return new(alloc) MCheckObjCoercible(toCheck);
     }
 
     MDefinition* checkValue() {
         return getOperand(0);
     }
 };
 
+class MDebugCheckSelfHosted
+  : public MUnaryInstruction,
+    public BoxInputsPolicy::Data
+{
+    explicit MDebugCheckSelfHosted(MDefinition* toCheck)
+      : MUnaryInstruction(toCheck)
+    {
+        setGuard();
+        setResultType(MIRType_Value);
+        setResultTypeSet(toCheck->resultTypeSet());
+    }
+
+  public:
+    INSTRUCTION_HEADER(DebugCheckSelfHosted)
+    static MDebugCheckSelfHosted* New(TempAllocator& alloc, MDefinition* toCheck) {
+        return new(alloc) MDebugCheckSelfHosted(toCheck);
+    }
+
+    MDefinition* checkValue() {
+        return getOperand(0);
+    }
+};
+
 class MAsmJSNeg
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     MAsmJSNeg(MDefinition* op, MIRType type)
       : MUnaryInstruction(op)
     {
         setResultType(type);
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -274,17 +274,18 @@ namespace jit {
     _(UnknownValue)                                                         \
     _(LexicalCheck)                                                         \
     _(ThrowRuntimeLexicalError)                                             \
     _(GlobalNameConflictsCheck)                                             \
     _(Debugger)                                                             \
     _(NewTarget)                                                            \
     _(ArrowNewTarget)                                                       \
     _(CheckReturn)                                                          \
-    _(CheckObjCoercible)
+    _(CheckObjCoercible)                                                    \
+    _(DebugCheckSelfHosted)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class MDefinitionVisitor // interface i.e. pure abstract class
 {
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7383,12 +7383,20 @@ class LCheckReturn : public LCallInstruc
 class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
 {
   public:
     static const size_t CheckValue = 0;
 
     LIR_HEADER(CheckObjCoercible)
 };
 
+class LDebugCheckSelfHosted : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
+{
+  public:
+    static const size_t CheckValue = 0;
+
+    LIR_HEADER(DebugCheckSelfHosted)
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_shared_LIR_shared_h */
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -362,11 +362,12 @@
     _(AssertResultT)                \
     _(LexicalCheck)                 \
     _(ThrowRuntimeLexicalError)     \
     _(GlobalNameConflictsCheck)     \
     _(Debugger)                     \
     _(NewTarget)                    \
     _(ArrowNewTarget)               \
     _(CheckReturn)                  \
-    _(CheckObjCoercible)
+    _(CheckObjCoercible)            \
+    _(DebugCheckSelfHosted)
 
 #endif /* jit_shared_LOpcodes_shared_h */
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -102,16 +102,17 @@ MSG_DEF(JSMSG_CANT_SET_PROTO,          0
 MSG_DEF(JSMSG_CANT_SET_PROTO_OF,       1, JSEXN_TYPEERR, "can't set prototype of {0}")
 MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE,    0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,        3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,              1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL,     1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
 MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
 MSG_DEF(JSMSG_UNINITIALIZED_THIS,      1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
 MSG_DEF(JSMSG_BAD_DERIVED_RETURN,      1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
+MSG_DEF(JSMSG_NOT_SELFHOSTED,          1, JSEXN_TYPEERR, "callFunction() used on non self-hosted builtin {0}")
 
 // JSON
 MSG_DEF(JSMSG_JSON_BAD_PARSE,          3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
 MSG_DEF(JSMSG_JSON_CYCLIC_VALUE,       1, JSEXN_TYPEERR, "cyclic {0} value")
 
 // Runtime errors
 MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS,      1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
 MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS,     0, JSEXN_REFERENCEERR, "invalid assignment left-hand side")
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -28,16 +28,17 @@
     macro(builder, builder, "builder") \
     macro(by, by, "by") \
     macro(byteLength, byteLength, "byteLength") \
     macro(byteAlignment, byteAlignment, "byteAlignment") \
     macro(byteOffset, byteOffset, "byteOffset") \
     macro(bytes, bytes, "bytes") \
     macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \
     macro(call, call, "call") \
+    macro(callContentFunction, callContentFunction, "callContentFunction") \
     macro(callee, callee, "callee") \
     macro(caller, caller, "caller") \
     macro(callFunction, callFunction, "callFunction") \
     macro(caseFirst, caseFirst, "caseFirst") \
     macro(class_, class_, "class") \
     macro(close, close, "close") \
     macro(Collator, Collator, "Collator") \
     macro(CollatorCompareGet, CollatorCompareGet, "Intl_Collator_compare_get") \
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -170,16 +170,32 @@ js::GetNonSyntacticGlobalThis(JSContext*
             return true;
         }
         scope = scope->enclosingScope();
     }
 
     return true;
 }
 
+bool
+js::Debug_CheckSelfHosted(JSContext* cx, HandleValue fun)
+{
+#ifndef DEBUG
+    MOZ_CRASH("Self hosted checks should only be done in Debug builds");
+#endif
+
+    MOZ_ASSERT(fun.isObject());
+
+    MOZ_ASSERT(fun.toObject().is<JSFunction>());
+    MOZ_ASSERT(fun.toObject().as<JSFunction>().isSelfHostedOrIntrinsic());
+
+    // This is purely to police self-hosted code. There is no actual operation.
+    return true;
+}
+
 static inline bool
 GetPropertyOperation(JSContext* cx, InterpreterFrame* fp, HandleScript script, jsbytecode* pc,
                      MutableHandleValue lval, MutableHandleValue vp)
 {
     JSOp op = JSOp(*pc);
 
     if (op == JSOP_LENGTH) {
         if (IsOptimizedArguments(fp, lval)) {
@@ -1735,17 +1751,16 @@ CASE(EnableInterruptsPseudoOpcode)
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED14)
 CASE(JSOP_UNUSED65)
 CASE(JSOP_BACKPATCH)
-CASE(JSOP_UNUSED177)
 CASE(JSOP_UNUSED178)
 CASE(JSOP_UNUSED179)
 CASE(JSOP_UNUSED180)
 CASE(JSOP_UNUSED181)
 CASE(JSOP_UNUSED182)
 CASE(JSOP_UNUSED183)
 CASE(JSOP_UNUSED187)
 CASE(JSOP_UNUSED192)
@@ -3886,16 +3901,26 @@ END_CASE(JSOP_CLASSCONSTRUCTOR)
 CASE(JSOP_CHECKOBJCOERCIBLE)
 {
     ReservedRooted<Value> checkVal(&rootValue0, REGS.sp[-1]);
     if (checkVal.isNullOrUndefined() && !ToObjectFromStack(cx, checkVal))
         goto error;
 }
 END_CASE(JSOP_CHECKOBJCOERCIBLE)
 
+CASE(JSOP_DEBUGCHECKSELFHOSTED)
+{
+#ifdef DEBUG
+    ReservedRooted<Value> checkVal(&rootValue0, REGS.sp[-1]);
+    if (!Debug_CheckSelfHosted(cx, checkVal))
+        goto error;
+#endif
+}
+END_CASE(JSOP_DEBUGCHECKSELFHOSTED)
+
 DEFAULT()
 {
     char numBuf[12];
     JS_snprintf(numBuf, sizeof numBuf, "%d", *REGS.pc);
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                          JSMSG_BAD_BYTECODE, numBuf);
     goto error;
 }
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -479,11 +479,14 @@ bool
 ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);
 
 bool
 DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp);
 
 bool
 DefaultDerivedClassConstructor(JSContext* cx, unsigned argc, Value* vp);
 
+bool
+Debug_CheckSelfHosted(JSContext* cx, HandleValue v);
+
 }  /* namespace js */
 
 #endif /* vm_Interpreter_h */
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1804,17 +1804,17 @@ 1234567890123456789012345678901234567890
     /*
      * Gets the value of a module import by name and pushes it onto the stack.
      *   Category: Variables and Scopes
      *   Type: Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => val
      */ \
     macro(JSOP_GETIMPORT,     176,"getimport",  NULL,     5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_TYPESET) \
-    macro(JSOP_UNUSED177,     177,"unused177",  NULL,     1,  0,  0,  JOF_BYTE) \
+    macro(JSOP_DEBUGCHECKSELFHOSTED, 177,"debug-checkselfhosted",  NULL, 1,  1,  1,  JOF_BYTE) \
     macro(JSOP_UNUSED178,     178,"unused178",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED179,     179,"unused179",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED180,     180,"unused180",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED181,     181,"unused181",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED182,     182,"unused182",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED183,     183,"unused183",  NULL,     1,  0,  0,  JOF_BYTE) \
     \
     /*
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 331;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 332;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 420,
+static_assert(JSErr_Limit == 421,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)