Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 26 Feb 2017 10:48:26 -0800
changeset 393950 7ef1e9abd296a8edc39b7efc8d637767ba2f77ed
parent 393938 aa2b14b08dbb728ab6b9868e43a66842626f1833 (current diff)
parent 393949 712e84866cf557b5ed88c7b991dd508ec3d550ef (diff)
child 393956 8e332422e57c39ee72bc7493875fad72f23dc640
child 393976 99239977e6c3a9428a037a9b1fd20f832adc5b09
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly mac
7ef1e9abd296 / 54.0a1 / 20170227030203 / files
nightly win32
7ef1e9abd296 / 54.0a1 / 20170227030203 / files
nightly win64
7ef1e9abd296 / 54.0a1 / 20170227030203 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
layout/reftests/css-grid/reftest-stylo.list
--- a/accessible/windows/msaa/DocAccessibleWrap.cpp
+++ b/accessible/windows/msaa/DocAccessibleWrap.cpp
@@ -122,16 +122,20 @@ DocAccessibleWrap::Shutdown()
 ////////////////////////////////////////////////////////////////////////////////
 // DocAccessible public
 
 void*
 DocAccessibleWrap::GetNativeWindow() const
 {
   if (XRE_IsContentProcess()) {
     DocAccessibleChild* ipcDoc = IPCDoc();
+    if (!ipcDoc) {
+      return nullptr;
+    }
+
     HWND hWnd = ipcDoc->GetEmulatedWindowHandle();
     if (hWnd) {
       return hWnd;
     }
 
     auto tab = static_cast<dom::TabChild*>(ipcDoc->Manager());
     MOZ_ASSERT(tab);
     if (!tab) {
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -27,16 +27,17 @@ const App = createClass({
 
   propTypes: {
     boxModel: PropTypes.shape(Types.boxModel).isRequired,
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
     showBoxModelProperties: PropTypes.bool.isRequired,
+    showGridOutline: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
--- a/devtools/client/inspector/layout/components/Grid.js
+++ b/devtools/client/inspector/layout/components/Grid.js
@@ -4,72 +4,86 @@
 
 "use strict";
 
 const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const GridDisplaySettings = createFactory(require("./GridDisplaySettings"));
 const GridList = createFactory(require("./GridList"));
+const GridOutline = createFactory(require("./GridOutline"));
 
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
 module.exports = createClass({
 
   displayName: "Grid",
 
   propTypes: {
     getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
     setSelectedNode: PropTypes.func.isRequired,
+    showGridOutline: PropTypes.bool.isRequired,
     onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   render() {
     let {
       getSwatchColorPickerTooltip,
       grids,
       highlighterSettings,
       setSelectedNode,
+      showGridOutline,
       onHideBoxModelHighlighter,
       onSetGridOverlayColor,
       onShowBoxModelHighlighterForNode,
       onToggleGridHighlighter,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
     } = this.props;
 
     return grids.length ?
       dom.div(
         {
           id: "layout-grid-container",
         },
-        GridList({
-          getSwatchColorPickerTooltip,
-          grids,
-          setSelectedNode,
-          onHideBoxModelHighlighter,
-          onSetGridOverlayColor,
-          onShowBoxModelHighlighterForNode,
-          onToggleGridHighlighter,
-        }),
-        GridDisplaySettings({
-          highlighterSettings,
-          onToggleShowGridLineNumbers,
-          onToggleShowInfiniteLines,
-        })
+        showGridOutline ?
+          GridOutline({
+            grids,
+          })
+          :
+          null,
+        dom.div(
+          {
+            className: "grid-content",
+          },
+          GridList({
+            getSwatchColorPickerTooltip,
+            grids,
+            setSelectedNode,
+            onHideBoxModelHighlighter,
+            onSetGridOverlayColor,
+            onShowBoxModelHighlighterForNode,
+            onToggleGridHighlighter,
+          }),
+          GridDisplaySettings({
+            highlighterSettings,
+            onToggleShowGridLineNumbers,
+            onToggleShowInfiniteLines,
+          })
+        )
       )
       :
       dom.div(
         {
           className: "layout-no-grids",
         },
         getStr("layout.noGrids")
       );
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/components/GridOutline.js
@@ -0,0 +1,123 @@
+/* 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/. */
+
+"use strict";
+
+const { addons, createClass, DOM: dom, PropTypes } =
+  require("devtools/client/shared/vendor/react");
+
+const Types = require("../types");
+
+// Move SVG grid to the right 100 units, so that it is not flushed against the edge of
+// layout border
+const TRANSLATE_X = -100;
+const TRANSLATE_Y = 0;
+
+const VIEWPORT_HEIGHT = 100;
+const VIEWPORT_WIDTH = 450;
+
+module.exports = createClass({
+
+  displayName: "GridOutline",
+
+  propTypes: {
+    grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
+  },
+
+  mixins: [ addons.PureRenderMixin ],
+
+  getInitialState() {
+    return {
+      selectedGrids: [],
+    };
+  },
+
+  componentWillReceiveProps({ grids }) {
+    this.setState({
+      selectedGrids: grids.filter(grid => grid.highlighted),
+    });
+  },
+
+  renderGridCell(columnNumber, rowNumber, color) {
+    return dom.rect(
+      {
+        className: "grid-outline-cell",
+        x: columnNumber,
+        y: rowNumber,
+        width: 10,
+        height: 10,
+        stroke: color,
+      }
+    );
+  },
+
+  renderGridFragment({ color, gridFragments }) {
+    // TODO: We are drawing the first fragment since only one is currently being stored.
+    // In future we will need to iterate over all fragments of a grid.
+    const { rows, cols } = gridFragments[0];
+    const numOfColLines = cols.lines.length - 1;
+    const numOfRowLines = rows.lines.length - 1;
+    const rectangles = [];
+
+    // Draw a rectangle that acts as a border for the grid outline
+    const border = this.renderGridOutlineBorder(numOfRowLines, numOfColLines, color);
+    rectangles.push(border);
+
+    let x = 1;
+    let y = 1;
+    const width = 10;
+    const height = 10;
+
+    // Draw the cells within the grid outline border
+    for (let row = 0; row < numOfRowLines; row++) {
+      for (let col = 0; col < numOfColLines; col++) {
+        rectangles.push(this.renderGridCell(x, y, color));
+        x += width;
+      }
+
+      x = 1;
+      y += height;
+    }
+
+    return rectangles;
+  },
+
+  renderGridOutline(gridFragments) {
+    return dom.g(
+      {
+        className: "grid-cell-group",
+      },
+      gridFragments.map(gridFragment => this.renderGridFragment(gridFragment))
+    );
+  },
+
+  renderGridOutlineBorder(numberOfRows, numberOfCols, color) {
+    return dom.rect(
+      {
+        className: "grid-outline-border",
+        x: 1,
+        y: 1,
+        width: numberOfCols * 10,
+        height: numberOfRows * 10,
+        stroke: color,
+      }
+    );
+  },
+
+  render() {
+    return this.state.selectedGrids.length ?
+      dom.svg(
+        {
+          className: "grid-outline",
+          width: "100%",
+          height: 100,
+          viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${VIEWPORT_WIDTH} ${VIEWPORT_HEIGHT}`,
+        },
+        this.renderGridOutline(this.state.selectedGrids)
+      )
+      :
+      null;
+  },
+
+});
--- a/devtools/client/inspector/layout/components/moz.build
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -7,9 +7,10 @@
 DevToolsModules(
     'Accordion.css',
     'Accordion.js',
     'App.js',
     'Grid.js',
     'GridDisplaySettings.js',
     'GridItem.js',
     'GridList.js',
+    'GridOutline.js',
 )
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -24,16 +24,17 @@ const {
 
 const App = createFactory(require("./components/App"));
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
+const SHOW_GRID_OUTLINE_PREF = "devtools.gridinspector.showGridOutline";
 const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
 
 // Default grid colors.
 const GRID_COLORS = [
   "#05E4EE",
   "#BB9DFF",
   "#FFB53B",
   "#71F362",
@@ -109,16 +110,22 @@ LayoutView.prototype = {
       },
 
       /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
       showBoxModelProperties: true,
 
+      /**
+       * Shows the grid outline if user preferences are set to true, otherwise, hidden by
+       * default.
+       */
+      showGridOutline: Services.prefs.getBoolPref(SHOW_GRID_OUTLINE_PREF),
+
       onHideBoxModelHighlighter,
 
       /**
        * Handler for a change in the grid overlay color picker for a grid container.
        *
        * @param  {NodeFront} node
        *         The NodeFront of the grid container element for which the grid color is
        *         being updated.
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -69,16 +69,17 @@ pref("devtools.inspector.colorWidget.ena
 // Enable the Font Inspector
 pref("devtools.fontinspector.enabled", true);
 
 // Enable the Layout View
 pref("devtools.layoutview.enabled", false);
 
 // Grid highlighter preferences
 pref("devtools.gridinspector.showGridLineNumbers", false);
+pref("devtools.gridinspector.showGridOutline", false);
 pref("devtools.gridinspector.showInfiniteLines", false);
 
 // By how many times eyedropper will magnify pixels
 pref("devtools.eyedropper.zoom", 6);
 
 // Enable to collapse attributes that are too long.
 pref("devtools.markup.collapseAttributes", true);
 
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -35,20 +35,49 @@
 }
 
 /**
  * Grid Container
  */
 
 #layout-grid-container {
   display: flex;
+  flex-direction: column;
   margin: 5px;
 }
 
 /**
+ * Grid Content
+ */
+
+.grid-content {
+  display: flex;
+  flex: 1;
+}
+
+/**
+ * Grid Outline
+ */
+
+#grid-outline {
+  margin: 5px auto;
+}
+
+.grid-outline-border {
+  stroke-width: 0.75;
+  fill: none;
+}
+
+.grid-outline-cell {
+  fill: none;
+  pointer-events: all;
+  stroke-dasharray: 0.5, 2;
+}
+
+/**
  * Container when no grids are present
  */
 
 .layout-no-grids {
   font-style: italic;
   text-align: center;
   padding: 0.5em;
 }
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -176,16 +176,17 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_console_error_source_click.js]
 [browser_console_filters.js]
 [browser_console_iframe_messages.js]
 [browser_console_keyboard_accessibility.js]
 [browser_console_log_inspectable_object.js]
 [browser_console_native_getters.js]
 [browser_console_navigation_marker.js]
 [browser_console_netlogging.js]
+skip-if = true # Bug 1298364
 [browser_console_nsiconsolemessage.js]
 [browser_console_optimized_out_vars.js]
 [browser_console_private_browsing.js]
 skip-if = e10s # Bug 1042253 - webconsole e10s tests
 [browser_console_server_logging.js]
 [browser_console_variables_view.js]
 [browser_console_variables_view_filter.js]
 [browser_console_variables_view_dom_nodes.js]
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2829,19 +2829,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   AutoJSAPI jsapi;
   jsapi.Init();
   JSContext *cx = jsapi.cx();
 
   // Check if we're anywhere near the stack limit before we reach the
   // transplanting code, since it has no good way to handle errors. This uses
   // the untrusted script limit, which is not strictly necessary since no
   // actual script should run.
-  bool overrecursed = false;
-  JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, overrecursed = true);
-  if (overrecursed) {
+  if (!js::CheckRecursionLimitConservativeDontReport(cx)) {
     NS_WARNING("Overrecursion in SetNewDocument");
     return NS_ERROR_FAILURE;
   }
 
   if (!mDoc) {
     // First document load.
 
     // Get our private root. If it is equal to us, then we need to
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2114,17 +2114,19 @@ nsresult
 ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
 {
   js::AssertSameCompartment(aCx, aObjArg);
 
   // Check if we're anywhere near the stack limit before we reach the
   // transplanting code, since it has no good way to handle errors. This uses
   // the untrusted script limit, which is not strictly necessary since no
   // actual script should run.
-  JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
+  if (!js::CheckRecursionLimitConservative(aCx)) {
+    return NS_ERROR_FAILURE;
+  }
 
   JS::Rooted<JSObject*> aObj(aCx, aObjArg);
   const DOMJSClass* domClass = GetDOMClass(aObj);
 
   // DOM things are always parented to globals.
   JS::Rooted<JSObject*> oldParent(aCx,
                                   js::GetGlobalForObjectCrossCompartment(aObj));
   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(oldParent) == oldParent);
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -109,17 +109,19 @@ js::obj_propertyIsEnumerable(JSContext* 
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static bool
 obj_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JS_CHECK_RECURSION(cx, return false);
+
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     JSString* str = ObjectToSource(cx, obj);
     if (!str)
         return false;
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2367,17 +2367,19 @@ ASTSerializer::classDefinition(ParseNode
     return optExpression(pn->pn_kid2, &heritage) &&
            statement(pn->pn_kid3, &classBody) &&
            builder.classDefinition(expr, className, heritage, classBody, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
+
     switch (pn->getKind()) {
       case PNK_FUNCTION:
       case PNK_VAR:
         return declaration(pn, dst);
 
       case PNK_LET:
       case PNK_CONST:
         return declaration(pn, dst);
@@ -2806,17 +2808,19 @@ ASTSerializer::generatorExpression(Parse
 
     return expression(next->pn_kid->pn_left, &body) &&
            builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
+
     switch (pn->getKind()) {
       case PNK_FUNCTION:
       {
         ASTType type = pn->pn_funbox->function()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR;
         return function(pn, type, dst);
       }
 
       case PNK_COMMA:
@@ -3369,17 +3373,19 @@ ASTSerializer::objectPattern(ParseNode* 
     }
 
     return builder.objectPattern(elts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::pattern(ParseNode* pn, MutableHandleValue dst)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
+
     switch (pn->getKind()) {
       case PNK_OBJECT:
         return objectPattern(pn, dst);
 
       case PNK_ARRAY:
         return arrayPattern(pn, dst);
 
       default:
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -239,19 +239,19 @@ class LoopControl : public BreakableCont
         MOZ_ASSERT(is<LoopControl>());
 
         LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing());
 
         stackDepth_ = bce->stackDepth;
         loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;
 
         int loopSlots;
-        if (loopKind == StatementKind::Spread)
+        if (loopKind == StatementKind::Spread || loopKind == StatementKind::ForOfLoop)
             loopSlots = 3;
-        else if (loopKind == StatementKind::ForInLoop || loopKind == StatementKind::ForOfLoop)
+        else if (loopKind == StatementKind::ForInLoop)
             loopSlots = 2;
         else
             loopSlots = 0;
 
         MOZ_ASSERT(loopSlots <= stackDepth_);
 
         if (enclosingLoop) {
             canIonOsr_ = (enclosingLoop->canIonOsr_ &&
@@ -273,74 +273,16 @@ class LoopControl : public BreakableCont
         MOZ_ASSERT(continueTarget.offset != -1);
         if (!patchBreaks(bce))
             return false;
         bce->patchJumpsToTarget(continues, continueTarget);
         return true;
     }
 };
 
-class ForOfLoopControl : public LoopControl
-{
-    // The stack depth of the iterator.
-    int32_t iterDepth_;
-
-    // for-of loops, when throwing from non-iterator code (i.e. from the body
-    // or from evaluating the LHS of the loop condition), need to call
-    // IteratorClose. If IteratorClose itself throws, we must not re-call
-    // IteratorClose. Since non-local jumps like break and return call
-    // IteratorClose, whenever a non-local jump is emitted, we must terminate
-    // the current JSTRY_ITERCLOSE note to skip the non-local jump code, then
-    // start a new one.
-    //
-    // Visually,
-    //
-    //   for (x of y) {
-    //     ...             instantiate ForOfLoopControl
-    //     ...         +   <-- iterCloseTryStart_ points to right before
-    //     ...                 assignment to loop variable
-    //     ...         ^
-    //     ...         |
-    //     if (...)    v
-    //                 +   call finishIterCloseTryNote before |break|
-    //                     above range is noted with JSTRY_ITERCLOSE
-    //
-    //       break;    <-- break and IteratorClose are not inside
-    //                     JSTRY_ITERCLOSE note
-    //
-    //                     call startNewIterCloseTryNote after |break|
-    //                 +   <-- next iterCloseTryStart_ points here
-    //     ...         |
-    //     ...         ~
-    //   }
-    ptrdiff_t iterCloseTryStart_;
-
-  public:
-    ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth)
-      : LoopControl(bce, StatementKind::ForOfLoop),
-        iterDepth_(iterDepth),
-        iterCloseTryStart_(-1)
-    {
-        MOZ_ASSERT(bce->stackDepth >= iterDepth);
-    }
-
-    MOZ_MUST_USE bool finishIterCloseTryNote(BytecodeEmitter* bce) {
-        ptrdiff_t end = bce->offset();
-        MOZ_ASSERT(end >= iterCloseTryStart_);
-        if (end != iterCloseTryStart_)
-            return bce->tryNoteList.append(JSTRY_ITERCLOSE, iterDepth_, iterCloseTryStart_, end);
-        return true;
-    }
-
-    void startNewIterCloseTryNote(BytecodeEmitter* bce) {
-        MOZ_ASSERT(bce->offset() > iterCloseTryStart_);
-        iterCloseTryStart_ = bce->offset();
-    }
-};
-
 class TryFinallyControl : public BytecodeEmitter::NestableControl
 {
     bool emittingSubroutine_;
 
   public:
     // The subroutine when emitting a finally block.
     JumpList gosubs;
 
@@ -2052,16 +1994,154 @@ class MOZ_STACK_CLASS IfThenElseEmitter
     }
 
     int32_t popped() const {
         return -pushed_;
     }
 #endif
 };
 
+class ForOfLoopControl : public LoopControl
+{
+    // The stack depth of the iterator.
+    int32_t iterDepth_;
+
+    // for-of loops, when throwing from non-iterator code (i.e. from the body
+    // or from evaluating the LHS of the loop condition), need to call
+    // IteratorClose.  This is done by enclosing non-iterator code with
+    // try-catch and call IteratorClose in `catch` block.
+    // If IteratorClose itself throws, we must not re-call IteratorClose. Since
+    // non-local jumps like break and return call IteratorClose, whenever a
+    // non-local jump is emitted, we must tell catch block not to perform
+    // IteratorClose.
+    //
+    //   for (x of y) {
+    //     // Operations for iterator (IteratorNext etc) are outside of
+    //     // try-block.
+    //     try {
+    //       ...
+    //       if (...) {
+    //         // Before non-local jump, clear iterator on the stack to tell
+    //         // catch block not to perform IteratorClose.
+    //         tmpIterator = iterator;
+    //         iterator = undefined;
+    //         IteratorClose(tmpIterator, { break });
+    //         break;
+    //       }
+    //       ...
+    //     } catch (e) {
+    //       // Just throw again when iterator is cleared by non-local jump.
+    //       if (iterator === undefined)
+    //         throw e;
+    //       IteratorClose(iterator, { throw, e });
+    //     }
+    //   }
+    Maybe<TryEmitter> tryCatch_;
+
+    bool allowSelfHosted_;
+
+  public:
+    ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted)
+      : LoopControl(bce, StatementKind::ForOfLoop),
+        iterDepth_(iterDepth),
+        allowSelfHosted_(allowSelfHosted)
+    {
+    }
+
+    bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
+        tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal);
+
+        if (!tryCatch_->emitTry())
+            return false;
+        return true;
+    }
+
+    bool emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) {
+        if (!tryCatch_->emitCatch())              // ITER ...
+            return false;
+
+        if (!bce->emit1(JSOP_EXCEPTION))          // ITER ... EXCEPTION
+            return false;
+        unsigned slotFromTop = bce->stackDepth - iterDepth_;
+        if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
+            return false;
+
+        // If ITER is undefined, it means the exception is thrown by
+        // IteratorClose for non-local jump, and we should't perform
+        // IteratorClose again here.
+        if (!bce->emit1(JSOP_UNDEFINED))          // ITER ... EXCEPTION ITER UNDEF
+            return false;
+        if (!bce->emit1(JSOP_STRICTNE))           // ITER ... EXCEPTION NE
+            return false;
+
+        IfThenElseEmitter ifIteratorIsNotClosed(bce);
+        if (!ifIteratorIsNotClosed.emitIf())      // ITER ... EXCEPTION
+            return false;
+
+        MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_));
+        if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
+            return false;
+        if (!emitIteratorClose(bce, CompletionKind::Throw)) // ITER ... EXCEPTION
+            return false;
+
+        if (!ifIteratorIsNotClosed.emitEnd())     // ITER ... EXCEPTION
+            return false;
+
+        if (!bce->emit1(JSOP_THROW))              // ITER ...
+            return false;
+
+        if (!tryCatch_->emitEnd())
+            return false;
+
+        tryCatch_.reset();
+        return true;
+    }
+
+    bool emitIteratorClose(BytecodeEmitter* bce,
+                           CompletionKind completionKind = CompletionKind::Normal) {
+        return bce->emitIteratorClose(completionKind, allowSelfHosted_);
+    }
+
+    bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) {
+        // Pop unnecessary values from the stack.  Effectively this means
+        // leaving try-catch block.  However, the performing IteratorClose can
+        // reach the depth for try-catch, and effectively re-enter the
+        // try-catch block.
+        if (!bce->emit1(JSOP_POP))                        // ITER RESULT
+            return false;
+        if (!bce->emit1(JSOP_POP))                        // ITER
+            return false;
+
+        // Clear ITER slot on the stack to tell catch block to avoid performing
+        // IteratorClose again.
+        if (!bce->emit1(JSOP_UNDEFINED))                  // ITER UNDEF
+            return false;
+        if (!bce->emit1(JSOP_SWAP))                       // UNDEF ITER
+            return false;
+
+        if (!emitIteratorClose(bce))                      // UNDEF
+            return false;
+
+        if (isTarget) {
+            // At the level of the target block, there's bytecode after the
+            // loop that will pop the iterator and the value, so push
+            // undefineds to balance the stack.
+            if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF
+                return false;
+            if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF UNDEF
+                return false;
+        } else {
+            if (!bce->emit1(JSOP_POP))                    //
+                return false;
+        }
+
+        return true;
+    }
+};
+
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                  Parser<FullParseHandler>* parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
                                  uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
@@ -2410,16 +2490,22 @@ BytecodeEmitter::emitDupAt(unsigned slot
 }
 
 bool
 BytecodeEmitter::emitCheckIsObj(CheckIsObjectKind kind)
 {
     return emit2(JSOP_CHECKISOBJ, uint8_t(kind));
 }
 
+bool
+BytecodeEmitter::emitCheckIsCallable(CheckIsCallableKind kind)
+{
+    return emit2(JSOP_CHECKISCALLABLE, uint8_t(kind));
+}
+
 static inline unsigned
 LengthOfSetLine(unsigned line)
 {
     return 1 /* SN_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
 }
 
 /* Updates line number notes, not column notes. */
 bool
@@ -2651,20 +2737,18 @@ NonLocalExitControl::leaveScope(Bytecode
 bool
 NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* target)
 {
     using NestableControl = BytecodeEmitter::NestableControl;
     using EmitterScope = BytecodeEmitter::EmitterScope;
 
     EmitterScope* es = bce_->innermostEmitterScope;
     int npops = 0;
-    bool hasForOfLoopsWithIteratorClose = false;
-
-    // IteratorClose is handled specially in the exception unwinder. For
-    // 'continue', 'break', and 'return' statements, emit IteratorClose
+
+    // For 'continue', 'break', and 'return' statements, emit IteratorClose
     // bytecode inline. 'continue' statements do not call IteratorClose for
     // the loop they are continuing.
     bool emitIteratorClose = kind_ == Continue || kind_ == Break || kind_ == Return;
     bool emitIteratorCloseAtTarget = emitIteratorClose && kind_ != Continue;
 
     auto flushPops = [&npops](BytecodeEmitter* bce) {
         if (npops && !bce->flushPops(&npops))
             return false;
@@ -2691,41 +2775,32 @@ NonLocalExitControl::prepareForNonLocalJ
                 /*
                  * There's a [exception or hole, retsub pc-index] pair and the
                  * possible return value on the stack that we need to pop.
                  */
                 npops += 3;
             } else {
                 if (!flushPops(bce_))
                     return false;
-                if (!bce_->emitJump(JSOP_GOSUB, &finallyControl.gosubs))
+                if (!bce_->emitJump(JSOP_GOSUB, &finallyControl.gosubs)) // ...
                     return false;
             }
             break;
           }
 
           case StatementKind::ForOfLoop:
-            if (!flushPops(bce_))
-                return false;
-
-            // The iterator and the current value are on the stack.
-            //
             if (emitIteratorClose) {
-                hasForOfLoopsWithIteratorClose = true;
-                if (!control->as<ForOfLoopControl>().finishIterCloseTryNote(bce_))
+                if (!flushPops(bce_))
                     return false;
-                if (!bce_->emit1(JSOP_POP))               // ... ITER
-                    return false;
-                if (!bce_->emitIteratorClose())           // ...
+
+                ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
+                if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
                     return false;
             } else {
-                if (!bce_->emit1(JSOP_POP))               // ... ITER
-                    return false;
-                if (!bce_->emit1(JSOP_POP))               // ...
-                    return false;
+                npops += 3;
             }
             break;
 
           case StatementKind::ForInLoop:
             if (!flushPops(bce_))
                 return false;
 
             // The iterator and the current value are on the stack.
@@ -2738,51 +2813,28 @@ NonLocalExitControl::prepareForNonLocalJ
           default:
             break;
         }
     }
 
     if (!flushPops(bce_))
         return false;
 
-    if (target && target->is<ForOfLoopControl>() && emitIteratorCloseAtTarget) {
-        hasForOfLoopsWithIteratorClose = true;
-        if (!target->as<ForOfLoopControl>().finishIterCloseTryNote(bce_))
-            return false;
-
-        // The iterator and the current value are on the stack. At the level
-        // of the target block, there's bytecode after the loop that will pop
-        // the iterator and the value, so duplicate the iterator and call
-        // IteratorClose.
-        if (!bce_->emitDupAt(1))                          // ... ITER RESULT ITER
-            return false;
-        if (!bce_->emitIteratorClose())                   // ... ITER RESULT
+    if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
+        ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
+        if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF
             return false;
     }
 
     EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope;
     for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
         if (!leaveScope(es))
             return false;
     }
 
-    // See comment in ForOfLoopControl.
-    if (hasForOfLoopsWithIteratorClose) {
-        for (NestableControl* control = bce_->innermostNestableControl;
-             control != target;
-             control = control->enclosing())
-        {
-            if (control->is<ForOfLoopControl>())
-                control->as<ForOfLoopControl>().startNewIterCloseTryNote(bce_);
-        }
-
-        if (target && target->is<ForOfLoopControl>() && emitIteratorCloseAtTarget)
-            target->as<ForOfLoopControl>().startNewIterCloseTryNote(bce_);
-    }
-
     return true;
 }
 
 }  // anonymous namespace
 
 bool
 BytecodeEmitter::emitGoto(NestableControl* target, JumpList* jumplist, SrcNoteType noteType)
 {
@@ -2986,17 +3038,18 @@ BytecodeEmitter::strictifySetNameOp(JSOp
         default:;
     }
     return op;
 }
 
 bool
 BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
  restart:
 
     switch (pn->getKind()) {
       // Trivial cases with no side effects.
       case PNK_NOP:
       case PNK_STRING:
       case PNK_TEMPLATE_STRING:
@@ -5176,17 +5229,17 @@ BytecodeEmitter::emitSetOrInitializeDest
         if (!emit1(JSOP_POP))
             return false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
+BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false */)
 {
     MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
                ".next() iteration is prohibited in self-hosted code because it "
                "can run user-modifiable iteration code");
 
     if (!emit1(JSOP_DUP))                                 // ... ITER ITER
         return false;
     if (!emitAtomOp(cx->names().next, JSOP_CALLPROP))     // ... ITER NEXT
@@ -5197,17 +5250,18 @@ BytecodeEmitter::emitIteratorNext(ParseN
         return false;
     if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT
         return false;
     checkTypeSet(JSOP_CALL);
     return true;
 }
 
 bool
-BytecodeEmitter::emitIteratorClose(bool allowSelfHosted)
+BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = CompletionKind::Normal */,
+                                   bool allowSelfHosted /* = false */)
 {
     MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
                ".close() on iterators is prohibited in self-hosted code because it "
                "can run user-modifiable iteration code");
 
     // Generate inline logic corresponding to IteratorClose (ES 7.4.6).
     //
     // Callers need to ensure that the iterator object is at the top of the
@@ -5230,27 +5284,96 @@ BytecodeEmitter::emitIteratorClose(bool 
         return false;
     if (!emit1(JSOP_UNDEFINED))                           // ... ITER RET RET UNDEFINED
         return false;
     if (!emit1(JSOP_NE))                                  // ... ITER RET ?NEQL
         return false;
     if (!ifReturnMethodIsDefined.emitIfElse())
         return false;
 
+    if (completionKind == CompletionKind::Throw) {
+        // 7.4.6 IteratorClose ( iterator, completion )
+        //   ...
+        //   3. Let return be ? GetMethod(iterator, "return").
+        //   4. If return is undefined, return Completion(completion).
+        //   5. Let innerResult be Call(return, iterator, « »).
+        //   6. If completion.[[Type]] is throw, return Completion(completion).
+        //   7. If innerResult.[[Type]] is throw, return
+        //      Completion(innerResult).
+        //
+        // For CompletionKind::Normal case, JSOP_CALL for step 5 checks if RET
+        // is callable, and throws if not.  Since step 6 doesn't match and
+        // error handling in step 3 and step 7 can be merged.
+        //
+        // For CompletionKind::Throw case, an error thrown by JSOP_CALL for
+        // step 5 is ignored by try-catch.  So we should check if RET is
+        // callable here, outside of try-catch, and the throw immediately if
+        // not.
+        CheckIsCallableKind kind = CheckIsCallableKind::IteratorReturn;
+        if (!emitCheckIsCallable(kind))                   // ... ITER RET
+            return false;
+    }
+
     // Steps 5, 8.
     //
     // Call "return" if it is not undefined or null, and check that it returns
     // an Object.
     if (!emit1(JSOP_SWAP))                                // ... RET ITER
         return false;
-    if (!emitCall(JSOP_CALL, 0))                          // ... RESULT
+
+    Maybe<TryEmitter> tryCatch;
+
+    if (completionKind == CompletionKind::Throw) {
+        tryCatch.emplace(this, TryEmitter::TryCatch, TryEmitter::DontUseRetVal,
+                         TryEmitter::DontUseControl);
+
+        // Mutate stack to balance stack for try-catch.
+        if (!emit1(JSOP_UNDEFINED))                       // ... RET ITER UNDEF
+            return false;
+        if (!tryCatch->emitTry())                         // ... RET ITER UNDEF
+            return false;
+        if (!emitDupAt(2))                                // ... RET ITER UNDEF RET
+            return false;
+        if (!emitDupAt(2))                                // ... RET ITER UNDEF RET ITER
+            return false;
+    }
+
+    if (!emitCall(JSOP_CALL, 0))                          // ... ... RESULT
         return false;
     checkTypeSet(JSOP_CALL);
-    if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RESULT
-        return false;
+
+    if (completionKind == CompletionKind::Throw) {
+        if (!emit1(JSOP_SWAP))                            // ... RET ITER RESULT UNDEF
+            return false;
+        if (!emit1(JSOP_POP))                             // ... RET ITER RESULT
+            return false;
+
+        if (!tryCatch->emitCatch())                       // ... RET ITER RESULT
+            return false;
+
+        // Just ignore the exception thrown by call.
+        if (!emit1(JSOP_EXCEPTION))                       // ... RET ITER RESULT EXC
+            return false;
+        if (!emit1(JSOP_POP))                             // ... RET ITER RESULT
+            return false;
+
+        if (!tryCatch->emitEnd())                         // ... RET ITER RESULT
+            return false;
+
+        // Restore stack.
+        if (!emit2(JSOP_UNPICK, 2))                       // ... RESULT RET ITER
+            return false;
+        if (!emit1(JSOP_POP))                             // ... RESULT RET
+            return false;
+        if (!emit1(JSOP_POP))                             // ... RESULT
+            return false;
+    } else {
+        if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RESULT
+            return false;
+    }
 
     if (!ifReturnMethodIsDefined.emitElse())
         return false;
     if (!emit1(JSOP_POP))                                 // ... ITER
         return false;
     if (!ifReturnMethodIsDefined.emitEnd())
         return false;
 
@@ -6797,60 +6920,73 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 {
     MOZ_ASSERT(forOfLoop->isKind(PNK_FOR));
     MOZ_ASSERT(forOfLoop->isArity(PN_BINARY));
 
     ParseNode* forOfHead = forOfLoop->pn_left;
     MOZ_ASSERT(forOfHead->isKind(PNK_FOROF));
     MOZ_ASSERT(forOfHead->isArity(PN_TERNARY));
 
+    ParseNode* forHeadExpr = forOfHead->pn_kid3;
+
+    // Certain builtins (e.g. Array.from) are implemented in self-hosting
+    // as for-of loops.
+    bool allowSelfHostedIter = false;
+    if (emitterMode == BytecodeEmitter::SelfHosting &&
+        forHeadExpr->isKind(PNK_CALL) &&
+        forHeadExpr->pn_head->name() == cx->names().allowContentIter)
+    {
+        allowSelfHostedIter = true;
+    }
+
     // Evaluate the expression being iterated.
-    ParseNode* forHeadExpr = forOfHead->pn_kid3;
     if (!emitTree(forHeadExpr))                           // ITERABLE
         return false;
     if (!emitIterator())                                  // ITER
         return false;
 
     int32_t iterDepth = stackDepth;
 
-    // For-of loops have both the iterator and the value on the stack. Push
-    // undefined to balance the stack.
+    // For-of loops have both the iterator, the result, and the result.value
+    // on the stack. Push undefineds to balance the stack.
     if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT
         return false;
-
-    ForOfLoopControl loopInfo(this, iterDepth);
+    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT UNDEF
+        return false;
+
+    ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter);
 
     // Annotate so IonMonkey can find the loop-closing jump.
     unsigned noteIndex;
     if (!newSrcNote(SRC_FOR_OF, &noteIndex))
         return false;
 
     JumpList initialJump;
-    if (!emitJump(JSOP_GOTO, &initialJump))               // ITER RESULT
+    if (!emitJump(JSOP_GOTO, &initialJump))               // ITER RESULT UNDEF
         return false;
 
     JumpTarget top{ -1 };
-    if (!emitLoopHead(nullptr, &top))                     // ITER RESULT
+    if (!emitLoopHead(nullptr, &top))                     // ITER RESULT UNDEF
         return false;
 
     // If the loop had an escaping lexical declaration, replace the current
     // environment with an dead zoned one to implement TDZ semantics.
     if (headLexicalEmitterScope) {
         // The environment chain only includes an environment for the for-of
         // loop head *if* a scope binding is captured, thereby requiring
         // recreation each iteration. If a lexical scope exists for the head,
         // it must be the innermost one. If that scope has closed-over
         // bindings inducing an environment, recreate the current environment.
         DebugOnly<ParseNode*> forOfTarget = forOfHead->pn_kid1;
         MOZ_ASSERT(forOfTarget->isKind(PNK_LET) || forOfTarget->isKind(PNK_CONST));
         MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope);
         MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical);
 
         if (headLexicalEmitterScope->hasEnvironment()) {
-            if (!emit1(JSOP_RECREATELEXICALENV))          // ITER RESULT
+            if (!emit1(JSOP_RECREATELEXICALENV))          // ITER RESULT UNDEF
                 return false;
         }
 
         // For uncaptured bindings, put them back in TDZ.
         if (!headLexicalEmitterScope->deadZoneFrameSlots(this))
             return false;
     }
 
@@ -6860,86 +6996,91 @@ BytecodeEmitter::emitForOf(ParseNode* fo
 #ifdef DEBUG
         auto loopDepth = this->stackDepth;
 #endif
 
         // Emit code to assign result.value to the iteration variable.
         //
         // Note that ES 13.7.5.13, step 5.c says getting result.value does not
         // call IteratorClose, so start JSTRY_ITERCLOSE after the GETPROP.
+        if (!emit1(JSOP_POP))                             // ITER RESULT
+            return false;
         if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
             return false;
         if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE
             return false;
 
-        loopInfo.startNewIterCloseTryNote(this);
+        if (!loopInfo.emitBeginCodeNeedingIteratorClose(this))
+            return false;
 
         if (!emitInitializeForInOrOfTarget(forOfHead))    // ITER RESULT VALUE
             return false;
 
-        if (!emit1(JSOP_POP))                             // ITER RESULT
-            return false;
-
         MOZ_ASSERT(stackDepth == loopDepth,
                    "the stack must be balanced around the initializing "
                    "operation");
 
+        // Remove VALUE from the stack to release it.
+        if (!emit1(JSOP_POP))                             // ITER RESULT
+            return false;
+        if (!emit1(JSOP_UNDEFINED))                       // ITER RESULT UNDEF
+            return false;
+
         // Perform the loop body.
         ParseNode* forBody = forOfLoop->pn_right;
-        if (!emitTree(forBody))                           // ITER RESULT
-            return false;
-
-        if (!loopInfo.finishIterCloseTryNote(this))
+        if (!emitTree(forBody))                           // ITER RESULT UNDEF
+            return false;
+
+        MOZ_ASSERT(stackDepth == loopDepth,
+                   "the stack must be balanced around the for-of body");
+
+        if (!loopInfo.emitEndCodeNeedingIteratorClose(this))
             return false;
 
         // Set offset for continues.
         loopInfo.continueTarget = { offset() };
 
-        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER RESULT
-            return false;
-
-        if (!emit1(JSOP_POP))                             // ITER
-            return false;
-        if (!emit1(JSOP_DUP))                             // ITER ITER
-            return false;
-
-        // Certain builtins (e.g. Array.from) are implemented in self-hosting
-        // as for-of loops.
-        bool allowSelfHostedIter = false;
-        if (emitterMode == BytecodeEmitter::SelfHosting &&
-            forHeadExpr->isKind(PNK_CALL) &&
-            forHeadExpr->pn_head->name() == cx->names().allowContentIter)
-        {
-            allowSelfHostedIter = true;
-        }
-
-        if (!emitIteratorNext(forOfHead, allowSelfHostedIter)) // ITER RESULT
-            return false;
-        if (!emit1(JSOP_DUP))                             // ITER RESULT RESULT
-            return false;
-        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT DONE
+        if (!emitLoopEntry(forHeadExpr, initialJump))     // ITER RESULT UNDEF
+            return false;
+
+        if (!emit1(JSOP_SWAP))                            // ITER UNDEF RESULT
+            return false;
+        if (!emit1(JSOP_POP))                             // ITER UNDEF
+            return false;
+        if (!emitDupAt(1))                                // ITER UNDEF ITER
+            return false;
+
+        if (!emitIteratorNext(forOfHead, allowSelfHostedIter)) // ITER UNDEF RESULT
+            return false;
+
+        if (!emit1(JSOP_SWAP))                            // ITER RESULT UNDEF
+            return false;
+
+        if (!emitDupAt(1))                                // ITER RESULT UNDEF RESULT
+            return false;
+        if (!emitAtomOp(cx->names().done, JSOP_GETPROP))  // ITER RESULT UNDEF DONE
             return false;
 
         if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
-            return false;                                 // ITER RESULT
+            return false;                                 // ITER RESULT UNDEF
 
         MOZ_ASSERT(this->stackDepth == loopDepth);
     }
 
     // Let Ion know where the closing jump of this loop is.
     if (!setSrcNoteOffset(noteIndex, 0, beq.offset - initialJump.offset))
         return false;
 
     if (!loopInfo.patchBreaksAndContinues(this))
         return false;
 
     if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
         return false;
 
-    return emitUint16Operand(JSOP_POPN, 2);               //
+    return emitUint16Operand(JSOP_POPN, 3);               //
 }
 
 bool
 BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitterScope)
 {
     MOZ_ASSERT(forInLoop->isKind(PNK_FOR));
     MOZ_ASSERT(forInLoop->isArity(PN_BINARY));
     MOZ_ASSERT(forInLoop->isOp(JSOP_ITER));
@@ -7339,16 +7480,18 @@ BytecodeEmitter::emitComprehensionForOf(
     if (!emitTree(forHeadExpr))                // EXPR
         return false;
     if (!emitIterator())                       // ITER
         return false;
 
     // Push a dummy result so that we properly enter iteration midstream.
     if (!emit1(JSOP_UNDEFINED))                // ITER RESULT
         return false;
+    if (!emit1(JSOP_UNDEFINED))                // ITER RESULT VALUE
+        return false;
 
     // Enter the block before the loop body, after evaluating the obj.
     // Initialize let bindings with undefined when entering, as the name
     // assigned to is a plain assignment.
     TDZCheckCache tdzCache(this);
     Maybe<EmitterScope> emitterScope;
     ParseNode* loopVariableName;
     if (lexicalScope) {
@@ -7376,52 +7519,69 @@ BytecodeEmitter::emitComprehensionForOf(
     if (!emitLoopHead(nullptr, &top))
         return false;
 
 #ifdef DEBUG
     int loopDepth = this->stackDepth;
 #endif
 
     // Emit code to assign result.value to the iteration variable.
+    if (!emit1(JSOP_POP))                                 // ITER RESULT
+        return false;
     if (!emit1(JSOP_DUP))                                 // ITER RESULT RESULT
         return false;
     if (!emitAtomOp(cx->names().value, JSOP_GETPROP))     // ITER RESULT VALUE
         return false;
+
+    // Notice: Comprehension for-of doesn't perform IteratorClose, since it's
+    // not in the spec.
+
     if (!emitAssignment(loopVariableName, JSOP_NOP, nullptr)) // ITER RESULT VALUE
         return false;
+
+    // Remove VALUE from the stack to release it.
     if (!emit1(JSOP_POP))                                 // ITER RESULT
         return false;
+    if (!emit1(JSOP_UNDEFINED))                           // ITER RESULT UNDEF
+        return false;
 
     // The stack should be balanced around the assignment opcode sequence.
     MOZ_ASSERT(this->stackDepth == loopDepth);
 
     // Emit code for the loop body.
-    if (!emitTree(forBody))
-        return false;
+    if (!emitTree(forBody))                               // ITER RESULT UNDEF
+        return false;
+
+    // The stack should be balanced around the assignment opcode sequence.
+    MOZ_ASSERT(this->stackDepth == loopDepth);
 
     // Set offset for continues.
     loopInfo.continueTarget = { offset() };
 
     if (!emitLoopEntry(forHeadExpr, jmp))
         return false;
 
-    if (!emit1(JSOP_POP))                                 // ITER
-        return false;
-    if (!emit1(JSOP_DUP))                                 // ITER ITER
-        return false;
-    if (!emitIteratorNext(forHead))                       // ITER RESULT
-        return false;
-    if (!emit1(JSOP_DUP))                                 // ITER RESULT RESULT
-        return false;
-    if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER RESULT DONE
+    if (!emit1(JSOP_SWAP))                                // ITER UNDEF RESULT
+        return false;
+    if (!emit1(JSOP_POP))                                 // ITER UNDEF
+        return false;
+    if (!emitDupAt(1))                                    // ITER UNDEF ITER
+        return false;
+    if (!emitIteratorNext(forHead))                       // ITER UNDEF RESULT
+        return false;
+    if (!emit1(JSOP_SWAP))                                // ITER RESULT UNDEF
+        return false;
+    if (!emitDupAt(1))                                    // ITER RESULT UNDEF RESULT
+        return false;
+    if (!emitAtomOp(cx->names().done, JSOP_GETPROP))      // ITER RESULT UNDEF DONE
         return false;
 
     JumpList beq;
     JumpTarget breakTarget{ -1 };
-    if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER RESULT
+    if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER RESULT UNDEF
         return false;
 
     MOZ_ASSERT(this->stackDepth == loopDepth);
 
     // Let Ion know where the closing jump of this loop is.
     if (!setSrcNoteOffset(noteIndex, 0, beq.offset - jmp.offset))
         return false;
 
@@ -7433,17 +7593,17 @@ BytecodeEmitter::emitComprehensionForOf(
 
     if (emitterScope) {
         if (!emitterScope->leave(this))
             return false;
         emitterScope.reset();
     }
 
     // Pop the result and the iter.
-    return emitUint16Operand(JSOP_POPN, 2);               //
+    return emitUint16Operand(JSOP_POPN, 3);               //
 }
 
 bool
 BytecodeEmitter::emitComprehensionForIn(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR));
 
     ParseNode* forHead = pn->pn_left;
@@ -10103,17 +10263,18 @@ BytecodeEmitter::emitClass(ParseNode* pn
     MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     EmitLevelManager elm(this);
 
     /* Emit notes to tell the current bytecode's source line number.
        However, a couple trees require special treatment; see the
        relevant emitter functions for details. */
     if (emitLineNote == EMIT_LINENOTE && !ParseNodeRequiresSpecialLineNumberNotes(pn)) {
         if (!updateLineNumberNotes(pn->pn_pos.begin))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -468,16 +468,19 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
     // Helper to emit JSOP_DUPAT. The argument is the value's depth on the
     // JS stack, as measured from the top.
     MOZ_MUST_USE bool emitDupAt(unsigned slotFromTop);
 
     // Helper to emit JSOP_CHECKISOBJ.
     MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
 
+    // Helper to emit JSOP_CHECKISCALLABLE.
+    MOZ_MUST_USE bool emitCheckIsCallable(CheckIsCallableKind kind);
+
     // Emit a bytecode followed by an uint16 immediate operand stored in
     // big-endian order.
     MOZ_MUST_USE bool emitUint16Operand(JSOp op, uint32_t operand);
 
     // Emit a bytecode followed by an uint32 immediate operand.
     MOZ_MUST_USE bool emitUint32Operand(JSOp op, uint32_t operand);
 
     // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
@@ -676,17 +679,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
     // emitIterator expects the iterable to already be on the stack.
     // It will replace that stack value with the corresponding iterator
     MOZ_MUST_USE bool emitIterator();
 
     // Pops iterator from the top of the stack. Pushes the result of |.next()|
     // onto the stack.
     MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
-    MOZ_MUST_USE bool emitIteratorClose(bool allowSelfHosted = false);
+    MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal,
+                                        bool allowSelfHosted = false);
 
     template <typename InnerEmitter>
     MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth,
                                                                 InnerEmitter emitter);
 
     // Check if the value on top of the stack is "undefined". If so, replace
     // that value on the stack with the value defined by |defaultExpr|.
     // |pattern| is a lhs node of the default expression.  If it's an
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -51,17 +51,18 @@ ListContainsHoistedDeclaration(JSContext
 //
 // THIS IS NOT A GENERAL-PURPOSE FUNCTION.  It is only written to work in the
 // specific context of deciding that |node|, as one arm of a PNK_IF controlled
 // by a constant condition, contains a declaration that forbids |node| being
 // completely eliminated as dead.
 static bool
 ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
   restart:
 
     // With a better-typed AST, we would have distinct parse node classes for
     // expressions and for statements and would characterize expressions with
     // ExpressionKind and statements with StatementKind.  Perhaps someday.  In
     // the meantime we must characterize every ParseNodeKind, even the
     // expression/sub-expression ones that, if we handle all statement kinds
@@ -1630,17 +1631,18 @@ FoldName(JSContext* cx, ParseNode* node,
         return true;
 
     return Fold(cx, &node->pn_expr, parser, inGenexpLambda);
 }
 
 bool
 Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     ParseNode* pn = *pnp;
 
     switch (pn->getKind()) {
       case PNK_NOP:
       case PNK_REGEXP:
       case PNK_STRING:
       case PNK_TRUE:
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3929,17 +3929,18 @@ Parser<ParseHandler>::maybeParseDirectiv
     }
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statementList(YieldHandling yieldHandling)
 {
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     Node pn = handler.newStatementList(pos());
     if (!pn)
         return null();
 
     bool canHaveDirectives = pc->atBodyLevel();
     if (canHaveDirectives)
         tokenStream.clearSawOctalEscape();
@@ -7158,17 +7159,18 @@ Parser<ParseHandler>::variableStatement(
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statement(YieldHandling yieldHandling)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     switch (tt) {
       // BlockStatement[?Yield, ?Return]
       case TOK_LC:
@@ -7357,17 +7359,18 @@ Parser<ParseHandler>::statement(YieldHan
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
                                         bool canHaveDirectives /* = false */)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     switch (tt) {
       // BlockStatement[?Yield, ?Return]
       case TOK_LC:
@@ -7832,17 +7835,18 @@ class AutoClearInDestructuringDecl
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                                  TripledotHandling tripledotHandling,
                                  PossibleError* possibleError /* = nullptr */,
                                  InvokedPrediction invoked /* = PredictUninvoked */)
 {
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
     // numeric arrays, such as some Kraken benchmarks, it happens more often.)
     //
@@ -8162,17 +8166,18 @@ Parser<ParseHandler>::unaryOpExpr(YieldH
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                                 PossibleError* possibleError /* = nullptr */,
                                 InvokedPrediction invoked /* = PredictUninvoked */)
 {
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
       case TOK_VOID:
         return unaryOpExpr(yieldHandling, PNK_VOID, JSOP_VOID, begin);
@@ -8475,17 +8480,18 @@ Parser<ParseHandler>::comprehensionIf(Ge
 
     return handler.newIfStatement(begin, cond, then, null());
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
 {
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
         return null();
     if (matched)
         return comprehensionFor(comprehensionKind);
 
     if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
@@ -8671,17 +8677,18 @@ Parser<ParseHandler>::memberExpr(YieldHa
                                  TokenKind tt, bool allowCallSyntax /* = true */,
                                  PossibleError* possibleError /* = nullptr */,
                                  InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     Node lhs;
 
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
         uint32_t newBegin = pos().begin;
         // Make sure this wasn't a |new.target| in disguise.
         Node newTarget;
         if (!tryNewTarget(newTarget))
             return null();
@@ -9670,17 +9677,18 @@ Parser<ParseHandler>::tryNewTarget(Node 
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                                   TokenKind tt, PossibleError* possibleError,
                                   InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
-    JS_CHECK_RECURSION(context, return null());
+    if (!CheckRecursionLimit(context))
+        return null();
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(invoked);
 
       case TOK_CLASS:
         return classDefinition(yieldHandling, ClassExpression, NameRequired);
 
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1215,17 +1215,20 @@ LoopChoiceNode::FilterASCII(int depth, b
 }
 
 // -------------------------------------------------------------------
 // Analysis
 
 void
 Analysis::EnsureAnalyzed(RegExpNode* that)
 {
-    JS_CHECK_RECURSION(cx, failASCII("Stack overflow"); return);
+    if (!CheckRecursionLimit(cx)) {
+        failASCII("Stack overflow");
+        return;
+    }
 
     if (that->info()->been_analyzed || that->info()->being_analyzed)
         return;
     that->info()->being_analyzed = true;
     that->Accept(this);
     that->info()->being_analyzed = false;
     that->info()->been_analyzed = true;
 }
@@ -2491,17 +2494,21 @@ BoyerMooreLookahead::EmitSkipInstruction
     masm->Bind(&cont);
 
     return true;
 }
 
 bool
 BoyerMooreLookahead::CheckOverRecursed()
 {
-    JS_CHECK_RECURSION(compiler()->cx(), compiler()->SetRegExpTooBig(); return false);
+    if (!CheckRecursionLimit(compiler()->cx())) {
+        compiler()->SetRegExpTooBig();
+        return false;
+    }
+
     return true;
 }
 
 // -------------------------------------------------------------------
 // Trace
 
 bool Trace::DeferredAction::Mentions(int that)
 {
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -512,23 +512,16 @@ HasLiveStackValueAtDepth(JSScript* scrip
 
           case JSTRY_FOR_OF:
             // For-of loops have both the iterator and the result object on
             // stack. The iterator is below the result object.
             if (stackDepth == tn->stackDepth - 1)
                 return true;
             break;
 
-          case JSTRY_ITERCLOSE:
-            // Code that need to call IteratorClose have the iterator on the
-            // stack.
-            if (stackDepth == tn->stackDepth)
-                return true;
-            break;
-
           case JSTRY_DESTRUCTURING_ITERCLOSE:
             // Destructuring code that need to call IteratorClose have both
             // the iterator and the "done" value on the stack.
             if (stackDepth == tn->stackDepth || stackDepth == tn->stackDepth - 1)
                 return true;
             break;
 
           default:
@@ -1662,17 +1655,18 @@ jit::BailoutIonToBaseline(JSContext* cx,
     // Do stack check.
     bool overRecursed = false;
     BaselineBailoutInfo *info = builder.info();
     uint8_t* newsp = info->incomingStack - (info->copyStackTop - info->copyStackBottom);
 #ifdef JS_SIMULATOR
     if (Simulator::Current()->overRecursed(uintptr_t(newsp)))
         overRecursed = true;
 #else
-    JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, newsp, overRecursed = true);
+    if (!CheckRecursionLimitWithStackPointerDontReport(cx, newsp))
+        overRecursed = true;
 #endif
     if (overRecursed) {
         JitSpew(JitSpew_BaselineBailouts, "  Overrecursion check failed!");
         return BAILOUT_RETURN_OVERRECURSED;
     }
 
     // Take the reconstructed baseline stack so it doesn't get freed when builder destructs.
     info = builder.takeBuffer();
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1383,16 +1383,36 @@ BaselineCompiler::emit_JSOP_CHECKISOBJ()
     pushArg(Imm32(GET_UINT8(pc)));
     if (!callVM(ThrowCheckIsObjectInfo))
         return false;
 
     masm.bind(&ok);
     return true;
 }
 
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+    FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+bool
+BaselineCompiler::emit_JSOP_CHECKISCALLABLE()
+{
+    frame.syncStack(0);
+    masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+    prepareVMCall();
+
+    pushArg(Imm32(GET_UINT8(pc)));
+    pushArg(R0);
+    if (!callVM(CheckIsCallableInfo))
+        return false;
+
+    return true;
+}
+
 typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
 static const VMFunction ThrowUninitializedThisInfo =
     FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
                                            "BaselineThrowUninitializedThis");
 
 bool
 BaselineCompiler::emit_JSOP_CHECKTHIS()
 {
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -213,16 +213,17 @@ namespace jit {
     _(JSOP_CALLEE)             \
     _(JSOP_GETRVAL)            \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)             \
     _(JSOP_FUNCTIONTHIS)       \
     _(JSOP_GLOBALTHIS)         \
     _(JSOP_CHECKISOBJ)         \
+    _(JSOP_CHECKISCALLABLE)    \
     _(JSOP_CHECKTHIS)          \
     _(JSOP_CHECKRETURN)        \
     _(JSOP_NEWTARGET)          \
     _(JSOP_SUPERCALL)          \
     _(JSOP_SPREADSUPERCALL)    \
     _(JSOP_THROWSETCONST)      \
     _(JSOP_THROWSETALIASEDCONST) \
     _(JSOP_THROWSETCALLEE) \
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -108,19 +108,21 @@ CheckFrame(InterpreterFrame* fp)
 static JitExecStatus
 EnterBaseline(JSContext* cx, EnterJitData& data)
 {
     if (data.osrFrame) {
         // Check for potential stack overflow before OSR-ing.
         uint8_t spDummy;
         uint32_t extra = BaselineFrame::Size() + (data.osrNumStackValues * sizeof(Value));
         uint8_t* checkSp = (&spDummy) - extra;
-        JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return JitExec_Aborted);
+        if (!CheckRecursionLimitWithStackPointer(cx, checkSp))
+            return JitExec_Aborted;
     } else {
-        JS_CHECK_RECURSION(cx, return JitExec_Aborted);
+        if (!CheckRecursionLimit(cx))
+            return JitExec_Aborted;
     }
 
 #ifdef DEBUG
     // Assert we don't GC before entering JIT code. A GC could discard JIT code
     // or move the function stored in the CalleeToken (it won't be traced at
     // this point). We use Maybe<> here so we can call reset() to call the
     // AutoAssertNoGC destructor before we enter JIT code.
     mozilla::Maybe<JS::AutoAssertNoGC> nogc;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11529,49 +11529,75 @@ class OutOfLineIsCallable : public OutOf
     void accept(CodeGenerator* codegen) {
         codegen->visitOutOfLineIsCallable(this);
     }
     LIsCallable* ins() const {
         return ins_;
     }
 };
 
-void
-CodeGenerator::visitIsCallable(LIsCallable* ins)
-{
-    Register object = ToRegister(ins->object());
-    Register output = ToRegister(ins->output());
-
-    OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
-    addOutOfLineCode(ool, ins->mir());
-
+template <CodeGenerator::CallableOrConstructor mode>
+void
+CodeGenerator::emitIsCallableOrConstructor(Register object, Register output, Label* failure)
+{
     Label notFunction, hasCOps, done;
     masm.loadObjClass(object, output);
 
-    // Just skim proxies off. Their notion of isCallable() is more complicated.
-    masm.branchTestClassIsProxy(true, output, ool->entry());
+    // Just skim proxies off. Their notion of isCallable()/isConstructor() is
+    // more complicated.
+    masm.branchTestClassIsProxy(true, output, failure);
 
     // An object is callable iff:
     //   is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call).
+    // An object is constructor iff:
+    //  ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
+    //   (getClass()->cOps && getClass()->cOps->construct)).
     masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
-    masm.move32(Imm32(1), output);
+    if (mode == Callable) {
+        masm.move32(Imm32(1), output);
+    } else {
+        Label notConstructor;
+        masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
+        masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
+        masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
+        masm.move32(Imm32(1), output);
+        masm.jump(&done);
+        masm.bind(&notConstructor);
+        masm.move32(Imm32(0), output);
+    }
     masm.jump(&done);
 
     masm.bind(&notFunction);
     masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
                    ImmPtr(nullptr), &hasCOps);
     masm.move32(Imm32(0), output);
     masm.jump(&done);
 
     masm.bind(&hasCOps);
     masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)),
+    size_t opsOffset = mode == Callable
+                       ? offsetof(js::ClassOps, call)
+                       : offsetof(js::ClassOps, construct);
+    masm.cmpPtrSet(Assembler::NonZero, Address(output, opsOffset),
                    ImmPtr(nullptr), output);
 
     masm.bind(&done);
+}
+
+void
+CodeGenerator::visitIsCallable(LIsCallable* ins)
+{
+    Register object = ToRegister(ins->object());
+    Register output = ToRegister(ins->output());
+
+    OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
+    addOutOfLineCode(ool, ins->mir());
+
+    emitIsCallableOrConstructor<Callable>(object, output, ool->entry());
+
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool)
 {
     LIsCallable* ins = ool->ins();
     Register object = ToRegister(ins->object());
@@ -11581,16 +11607,46 @@ CodeGenerator::visitOutOfLineIsCallable(
     masm.setupUnalignedABICall(output);
     masm.passABIArg(object);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ObjectIsCallable));
     masm.storeCallBoolResult(output);
     restoreVolatile(output);
     masm.jump(ool->rejoin());
 }
 
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+    FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+void
+CodeGenerator::visitCheckIsCallable(LCheckIsCallable* ins)
+{
+    ValueOperand checkValue = ToValue(ins, LCheckIsCallable::CheckValue);
+    Register temp = ToRegister(ins->temp());
+
+    // OOL code is used in the following 2 cases:
+    //   * checkValue is not callable
+    //   * checkValue is proxy and it's unknown whether it's callable or not
+    // CheckIsCallable checks if passed value is callable, regardless of the
+    // cases above.  IsCallable operation is not observable and checking it
+    // again doesn't matter.
+    OutOfLineCode* ool = oolCallVM(CheckIsCallableInfo, ins,
+                                   ArgList(checkValue, Imm32(ins->mir()->checkKind())),
+                                   StoreNothing());
+
+    masm.branchTestObject(Assembler::NotEqual, checkValue, ool->entry());
+
+    Register object = masm.extractObject(checkValue, temp);
+    emitIsCallableOrConstructor<Callable>(object, temp, ool->entry());
+
+    masm.branchTest32(Assembler::Zero, temp, temp, ool->entry());
+
+    masm.bind(ool->rejoin());
+}
+
 class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator>
 {
     LIsConstructor* ins_;
 
   public:
     explicit OutOfLineIsConstructor(LIsConstructor* ins)
       : ins_(ins)
     { }
@@ -11607,47 +11663,18 @@ void
 CodeGenerator::visitIsConstructor(LIsConstructor* ins)
 {
     Register object = ToRegister(ins->object());
     Register output = ToRegister(ins->output());
 
     OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins);
     addOutOfLineCode(ool, ins->mir());
 
-    Label notFunction, notConstructor, hasCOps, done;
-    masm.loadObjClass(object, output);
-
-    // Just skim proxies off. Their notion of isConstructor() is more complicated.
-    masm.branchTestClassIsProxy(true, output, ool->entry());
-
-    // An object is constructor iff
-    //  ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
-    //   (getClass()->cOps && getClass()->cOps->construct)).
-    masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
-    masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
-    masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
-    masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
-    masm.move32(Imm32(1), output);
-    masm.jump(&done);
-    masm.bind(&notConstructor);
-    masm.move32(Imm32(0), output);
-    masm.jump(&done);
-
-    masm.bind(&notFunction);
-    masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
-                   ImmPtr(nullptr), &hasCOps);
-    masm.move32(Imm32(0), output);
-    masm.jump(&done);
-
-    masm.bind(&hasCOps);
-    masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
-    masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)),
-                   ImmPtr(nullptr), output);
-
-    masm.bind(&done);
+    emitIsCallableOrConstructor<Constructor>(object, output, ool->entry());
+
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool)
 {
     LIsConstructor* ins = ool->ins();
     Register object = ToRegister(ins->object());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -363,16 +363,22 @@ class CodeGenerator final : public CodeG
     void visitCallInstanceOf(LCallInstanceOf* ins);
     void visitGetDOMProperty(LGetDOMProperty* lir);
     void visitGetDOMMemberV(LGetDOMMemberV* lir);
     void visitGetDOMMemberT(LGetDOMMemberT* lir);
     void visitSetDOMProperty(LSetDOMProperty* lir);
     void visitCallDOMNative(LCallDOMNative* lir);
     void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir);
     void visitCallBindVar(LCallBindVar* lir);
+    enum CallableOrConstructor {
+        Callable,
+        Constructor
+    };
+    template <CallableOrConstructor mode>
+    void emitIsCallableOrConstructor(Register object, Register output, Label* failure);
     void visitIsCallable(LIsCallable* lir);
     void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
     void visitIsConstructor(LIsConstructor* lir);
     void visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool);
     void visitIsObject(LIsObject* lir);
     void visitIsObjectAndBranch(LIsObjectAndBranch* lir);
     void visitHasClass(LHasClass* lir);
     void visitWasmParameter(LWasmParameter* lir);
@@ -383,16 +389,17 @@ class CodeGenerator final : public CodeG
     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 visitCheckIsObj(LCheckIsObj* ins);
+    void visitCheckIsCallable(LCheckIsCallable* ins);
     void visitCheckObjCoercible(LCheckObjCoercible* ins);
     void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
     void visitNaNToZero(LNaNToZero* ins);
     void visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool);
 
     void visitCheckOverRecursed(LCheckOverRecursed* lir);
     void visitCheckOverRecursedFailure(CheckOverRecursedFailure* ool);
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2854,17 +2854,19 @@ jit::CanEnterUsingFastInvoke(JSContext* 
         return Method_Skipped;
 
     return Method_Compiled;
 }
 
 static JitExecStatus
 EnterIon(JSContext* cx, EnterJitData& data)
 {
-    JS_CHECK_RECURSION(cx, return JitExec_Aborted);
+    if (!CheckRecursionLimit(cx))
+        return JitExec_Aborted;
+
     MOZ_ASSERT(jit::IsIonEnabled(cx));
     MOZ_ASSERT(!data.osrFrame);
 
 #ifdef DEBUG
     // See comment in EnterBaseline.
     mozilla::Maybe<JS::AutoAssertNoGC> nogc;
     nogc.emplace(cx);
 #endif
@@ -2988,17 +2990,18 @@ jit::IonCannon(JSContext* cx, RunState& 
         state.setReturnValue(data.result);
 
     return status;
 }
 
 JitExecStatus
 jit::FastInvoke(JSContext* cx, HandleFunction fun, CallArgs& args)
 {
-    JS_CHECK_RECURSION(cx, return JitExec_Error);
+    if (!CheckRecursionLimit(cx))
+        return JitExec_Error;
 
     RootedScript script(cx, fun->nonLazyScript());
 
     if (!Debugger::checkNoExecute(cx, script))
         return JitExec_Error;
 
 #ifdef DEBUG
     // See comment in EnterBaseline.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2262,16 +2262,19 @@ IonBuilder::inspectOpcode(JSOp op)
         break;
 
       case JSOP_NEWTARGET:
         return jsop_newtarget();
 
       case JSOP_CHECKISOBJ:
         return jsop_checkisobj(GET_UINT8(pc));
 
+      case JSOP_CHECKISCALLABLE:
+        return jsop_checkiscallable(GET_UINT8(pc));
+
       case JSOP_CHECKOBJCOERCIBLE:
         return jsop_checkobjcoercible();
 
       case JSOP_DEBUGCHECKSELFHOSTED:
       {
 #ifdef DEBUG
         MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), current->pop());
         current->add(check);
@@ -9415,16 +9418,25 @@ IonBuilder::jsop_checkisobj(uint8_t kind
 
     MCheckIsObj* check = MCheckIsObj::New(alloc(), current->pop(), kind);
     current->add(check);
     current->push(check);
     return Ok();
 }
 
 AbortReasonOr<Ok>
+IonBuilder::jsop_checkiscallable(uint8_t kind)
+{
+    MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
+    current->add(check);
+    current->push(check);
+    return Ok();
+}
+
+AbortReasonOr<Ok>
 IonBuilder::jsop_checkobjcoercible()
 {
     MDefinition* toCheck = current->peek(-1);
 
     if (!toCheck->mightBeType(MIRType::Undefined) &&
         !toCheck->mightBeType(MIRType::Null))
     {
         toCheck->setImplicitlyUsedUnchecked();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -574,16 +574,17 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_iterend();
     AbortReasonOr<Ok> jsop_in();
     AbortReasonOr<Ok> jsop_instanceof();
     AbortReasonOr<Ok> jsop_getaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_setaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_debugger();
     AbortReasonOr<Ok> jsop_newtarget();
     AbortReasonOr<Ok> jsop_checkisobj(uint8_t kind);
+    AbortReasonOr<Ok> jsop_checkiscallable(uint8_t kind);
     AbortReasonOr<Ok> jsop_checkobjcoercible();
     AbortReasonOr<Ok> jsop_pushcallobj();
 
     /* Inlining. */
 
     enum InliningStatus
     {
         InliningStatus_NotInlined,
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -326,17 +326,16 @@ NumArgAndLocalSlots(const InlineFrameIte
     JSScript* script = frame.script();
     return CountArgSlots(script, frame.maybeCalleeTemplate()) + script->nfixed();
 }
 
 static void
 CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, JSTryNote* tn)
 {
     MOZ_ASSERT(tn->kind == JSTRY_FOR_IN ||
-               tn->kind == JSTRY_ITERCLOSE ||
                tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE);
 
     bool isDestructuring = tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE;
     MOZ_ASSERT_IF(!isDestructuring, tn->stackDepth > 0);
     MOZ_ASSERT_IF(isDestructuring, tn->stackDepth > 1);
 
     SnapshotIterator si = frame.snapshotIterator();
 
@@ -437,17 +436,16 @@ HandleExceptionIon(JSContext* cx, const 
     if (!script->hasTrynotes())
         return;
 
     for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) {
         JSTryNote* tn = *tni;
 
         switch (tn->kind) {
           case JSTRY_FOR_IN:
-          case JSTRY_ITERCLOSE:
           case JSTRY_DESTRUCTURING_ITERCLOSE:
             MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN,
                           JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
             CloseLiveIteratorIon(cx, frame, tn);
             break;
 
           case JSTRY_FOR_OF:
           case JSTRY_LOOP:
@@ -638,29 +636,16 @@ ProcessTryNotesBaseline(JSContext* cx, c
                 // ProcessTryNotes.
                 SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
                 MOZ_ASSERT(**pc == JSOP_ENDITER);
                 return false;
             }
             break;
           }
 
-          case JSTRY_ITERCLOSE: {
-            uint8_t* framePointer;
-            uint8_t* stackPointer;
-            BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
-            Value iterValue(*reinterpret_cast<Value*>(stackPointer));
-            RootedObject iterObject(cx, &iterValue.toObject());
-            if (!IteratorCloseForException(cx, iterObject)) {
-                SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
-                return false;
-            }
-            break;
-          }
-
           case JSTRY_DESTRUCTURING_ITERCLOSE: {
             uint8_t* framePointer;
             uint8_t* stackPointer;
             BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
             RootedValue doneValue(cx, *(reinterpret_cast<Value*>(stackPointer)));
             bool done = ToBoolean(doneValue);
             if (!done) {
                 Value iterValue(*(reinterpret_cast<Value*>(stackPointer) + 1));
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4782,16 +4782,29 @@ LIRGenerator::visitCheckIsObj(MCheckIsOb
 
     LCheckIsObj* lir = new(alloc()) LCheckIsObj(useBoxAtStart(checkVal));
     redefine(ins, checkVal);
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitCheckIsCallable(MCheckIsCallable* ins)
+{
+    MDefinition* checkVal = ins->checkValue();
+    MOZ_ASSERT(checkVal->type() == MIRType::Value);
+
+    LCheckIsCallable* lir = new(alloc()) LCheckIsCallable(useBox(checkVal),
+                                                          temp());
+    redefine(ins, checkVal);
+    add(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
 LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins)
 {
     MDefinition* checkVal = ins->checkValue();
     MOZ_ASSERT(checkVal->type() == MIRType::Value);
 
     LCheckObjCoercible* lir = new(alloc()) LCheckObjCoercible(useBoxAtStart(checkVal));
     redefine(ins, checkVal);
     add(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -326,16 +326,17 @@ class LIRGenerator : public LIRGenerator
     void visitDebugger(MDebugger* ins);
     void visitNewTarget(MNewTarget* ins);
     void visitArrowNewTarget(MArrowNewTarget* ins);
     void visitNaNToZero(MNaNToZero *ins);
     void visitAtomicIsLockFree(MAtomicIsLockFree* ins);
     void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
     void visitCheckReturn(MCheckReturn* ins);
     void visitCheckIsObj(MCheckIsObj* ins);
+    void visitCheckIsCallable(MCheckIsCallable* 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
@@ -13511,18 +13511,19 @@ class MDebugger : public MNullaryInstruc
 };
 
 class MCheckIsObj
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
     uint8_t checkKind_;
 
-    explicit MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
-      : MUnaryInstruction(toCheck), checkKind_(checkKind)
+    MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
+      : MUnaryInstruction(toCheck),
+        checkKind_(checkKind)
     {
         setResultType(MIRType::Value);
         setResultTypeSet(toCheck->resultTypeSet());
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(CheckIsObj)
@@ -13531,16 +13532,43 @@ class MCheckIsObj
 
     uint8_t checkKind() const { return checkKind_; }
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
+class MCheckIsCallable
+  : public MUnaryInstruction,
+    public BoxInputsPolicy::Data
+{
+    uint8_t checkKind_;
+
+    MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind)
+      : MUnaryInstruction(toCheck),
+        checkKind_(checkKind)
+    {
+        setResultType(MIRType::Value);
+        setResultTypeSet(toCheck->resultTypeSet());
+        setGuard();
+    }
+
+  public:
+    INSTRUCTION_HEADER(CheckIsCallable)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, checkValue))
+
+    uint8_t checkKind() const { return checkKind_; }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+};
+
 class MCheckObjCoercible
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
     explicit MCheckObjCoercible(MDefinition* toCheck)
       : MUnaryInstruction(toCheck)
     {
         setGuard();
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -282,16 +282,17 @@ namespace jit {
     _(LexicalCheck)                                                         \
     _(ThrowRuntimeLexicalError)                                             \
     _(GlobalNameConflictsCheck)                                             \
     _(Debugger)                                                             \
     _(NewTarget)                                                            \
     _(ArrowNewTarget)                                                       \
     _(CheckReturn)                                                          \
     _(CheckIsObj)                                                           \
+    _(CheckIsCallable)                                                      \
     _(CheckObjCoercible)                                                    \
     _(DebugCheckSelfHosted)                                                 \
     _(AsmJSNeg)                                                             \
     _(AsmJSLoadHeap)                                                        \
     _(AsmJSStoreHeap)                                                       \
     _(AsmJSCompareExchangeHeap)                                             \
     _(AsmJSAtomicExchangeHeap)                                              \
     _(AsmJSAtomicBinopHeap)                                                 \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -118,27 +118,41 @@ bool
 InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs,
                                uint32_t numFormalArgs, Value* argv, MutableHandleValue rval)
 {
     MOZ_ASSERT(numFormalArgs > numActualArgs);
     argv[1 + numActualArgs] = argv[1 + numFormalArgs];
     return InvokeFunction(cx, obj, true, numActualArgs, argv, rval);
 }
 
+#ifdef JS_SIMULATOR
+static bool
+CheckSimulatorRecursionLimitWithExtra(JSContext* cx, uint32_t extra)
+{
+    if (cx->simulator()->overRecursedWithExtra(extra)) {
+        ReportOverRecursed(cx);
+        return false;
+    }
+    return true;
+}
+#endif
+
 bool
 CheckOverRecursed(JSContext* cx)
 {
     // We just failed the jitStackLimit check. There are two possible reasons:
     //  - jitStackLimit was the real stack limit and we're over-recursed
     //  - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
     //    and we need to call JSRuntime::handleInterrupt.
 #ifdef JS_SIMULATOR
-    JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
+    if (!CheckSimulatorRecursionLimitWithExtra(cx, 0))
+        return false;
 #else
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 #endif
     gc::MaybeVerifyBarriers(cx);
     return cx->handleInterrupt();
 }
 
 // This function can get called in two contexts.  In the usual context, it's
 // called with earlyCheck=false, after the env chain has been initialized on
 // a baseline frame.  In this case, it's ok to throw an exception, so a failed
@@ -157,32 +171,36 @@ CheckOverRecursedWithExtra(JSContext* cx
     // See |CheckOverRecursed| above.  This is a variant of that function which
     // accepts an argument holding the extra stack space needed for the Baseline
     // frame that's about to be pushed.
     uint8_t spDummy;
     uint8_t* checkSp = (&spDummy) - extra;
     if (earlyCheck) {
 #ifdef JS_SIMULATOR
         (void)checkSp;
-        JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, frame->setOverRecursed());
+        if (!CheckSimulatorRecursionLimitWithExtra(cx, extra))
+            frame->setOverRecursed();
 #else
-        JS_CHECK_RECURSION_WITH_SP(cx, checkSp, frame->setOverRecursed());
+        if (!CheckRecursionLimitWithStackPointer(cx, checkSp))
+            frame->setOverRecursed();
 #endif
         return true;
     }
 
     // The OVERRECURSED flag may have already been set on the frame by an
     // early over-recursed check.  If so, throw immediately.
     if (frame->overRecursed())
         return false;
 
 #ifdef JS_SIMULATOR
-    JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, return false);
+    if (!CheckSimulatorRecursionLimitWithExtra(cx, extra))
+        return false;
 #else
-    JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
+    if (!CheckRecursionLimitWithStackPointer(cx, checkSp))
+        return false;
 #endif
 
     gc::MaybeVerifyBarriers(cx);
     return cx->handleInterrupt();
 }
 
 JSObject*
 BindVar(JSContext* cx, HandleObject envChain)
@@ -1478,10 +1496,19 @@ EqualStringsHelper(JSString* str1, JSStr
 
     JSLinearString* str2Linear = str2->ensureLinear(nullptr);
     if (!str2Linear)
         return false;
 
     return EqualChars(&str1->asLinear(), str2Linear);
 }
 
+bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
+{
+    if (!IsCallable(v))
+        return ThrowCheckIsCallable(cx, kind);
+
+    return true;
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -8,16 +8,17 @@
 #define jit_VMFunctions_h
 
 #include "mozilla/Attributes.h"
 
 #include "jspubtd.h"
 
 #include "jit/CompileInfo.h"
 #include "jit/JitFrames.h"
+#include "vm/Interpreter.h"
 
 namespace js {
 
 class NamedLambdaObject;
 class WithScope;
 class InlineTypedObject;
 class GeneratorObject;
 class TypedArrayObject;
@@ -833,12 +834,15 @@ CallNativeGetter(JSContext* cx, HandleFu
 
 MOZ_MUST_USE bool
 CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj,
                  HandleValue rhs);
 
 MOZ_MUST_USE bool
 EqualStringsHelper(JSString* str1, JSString* str2);
 
+MOZ_MUST_USE bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -526,22 +526,14 @@ class SimulatorProcess
     }
 
     static void setRedirection(js::jit::Redirection* redirection) {
         MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
         singleton_->redirection_ = redirection;
     }
 };
 
-#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror)     \
-    JS_BEGIN_MACRO                                                              \
-        if (cx->simulator()->overRecursedWithExtra(extra)) {                    \
-            js::ReportOverRecursed(cx);                                         \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
 } // namespace jit
 } // namespace js
 
 #endif /* JS_SIMULATOR_ARM */
 
 #endif /* jit_arm_Simulator_arm_h */
--- a/js/src/jit/arm64/vixl/Simulator-vixl.h
+++ b/js/src/jit/arm64/vixl/Simulator-vixl.h
@@ -41,24 +41,16 @@
 #include "jit/arm64/vixl/Instructions-vixl.h"
 #include "jit/arm64/vixl/Instrument-vixl.h"
 #include "jit/arm64/vixl/Simulator-Constants-vixl.h"
 #include "jit/arm64/vixl/Utils-vixl.h"
 #include "jit/IonTypes.h"
 #include "vm/MutexIDs.h"
 #include "vm/PosixNSPR.h"
 
-#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror)             \
-    JS_BEGIN_MACRO                                                              \
-        if (cx->simulator()->overRecursedWithExtra(extra)) {                    \
-            js::ReportOverRecursed(cx);                                         \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
 namespace vixl {
 
 // Assemble the specified IEEE-754 components into the target type and apply
 // appropriate rounding.
 //  sign:     0 = positive, 1 = negative
 //  exponent: Unbiased IEEE-754 exponent.
 //  mantissa: The mantissa of the input. The top bit (which is not encoded for
 //            normal IEEE-754 values) must not be omitted. This bit has the
--- a/js/src/jit/mips32/Simulator-mips32.h
+++ b/js/src/jit/mips32/Simulator-mips32.h
@@ -433,22 +433,14 @@ class SimulatorProcess
     }
 
     static void setRedirection(js::jit::Redirection* redirection) {
         MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
         singleton_->redirection_ = redirection;
     }
 };
 
-#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror)             \
-    JS_BEGIN_MACRO                                                              \
-        if (cx->simulator()->overRecursedWithExtra(extra)) {                    \
-            js::ReportOverRecursed(cx);                                         \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
 } // namespace jit
 } // namespace js
 
 #endif /* JS_SIMULATOR_MIPS32 */
 
 #endif /* jit_mips32_Simulator_mips32_h */
--- a/js/src/jit/mips64/Simulator-mips64.h
+++ b/js/src/jit/mips64/Simulator-mips64.h
@@ -447,22 +447,14 @@ class SimulatorProcess
     }
 
     static void setRedirection(js::jit::Redirection* redirection) {
         MOZ_ASSERT(singleton_->cacheLock_.ownedByCurrentThread());
         singleton_->redirection_ = redirection;
     }
 };
 
-#define JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, onerror)     \
-    JS_BEGIN_MACRO                                                              \
-        if (cx->simulator()->overRecursedWithExtra(extra)) {                    \
-            js::ReportOverRecursed(cx);                                         \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
 } // namespace jit
 } // namespace js
 
 #endif /* JS_SIMULATOR_MIPS64 */
 
 #endif /* jit_mips64_Simulator_mips64_h */
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8987,16 +8987,37 @@ class LCheckIsObj : public LInstructionH
         setBoxOperand(CheckValue, value);
     }
 
     MCheckIsObj* mir() const {
         return mir_->toCheckIsObj();
     }
 };
 
+class LCheckIsCallable : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
+{
+  public:
+    LIR_HEADER(CheckIsCallable)
+
+    static const size_t CheckValue = 0;
+
+    LCheckIsCallable(const LBoxAllocation& value, const LDefinition& temp) {
+        setBoxOperand(CheckValue, value);
+        setTemp(0, temp);
+    }
+
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+
+    MCheckIsCallable* mir() const {
+        return mir_->toCheckIsCallable();
+    }
+};
+
 class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
 {
   public:
     LIR_HEADER(CheckObjCoercible)
 
     static const size_t CheckValue = 0;
 
     explicit LCheckObjCoercible(const LBoxAllocation& value) {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -400,16 +400,17 @@
     _(LexicalCheck)                 \
     _(ThrowRuntimeLexicalError)     \
     _(GlobalNameConflictsCheck)     \
     _(Debugger)                     \
     _(NewTarget)                    \
     _(ArrowNewTarget)               \
     _(CheckReturn)                  \
     _(CheckIsObj)                   \
+    _(CheckIsCallable)              \
     _(CheckObjCoercible)            \
     _(DebugCheckSelfHosted)         \
     _(AsmJSLoadHeap)                \
     _(AsmJSStoreHeap)               \
     _(AsmJSCompareExchangeHeap)     \
     _(AsmJSAtomicExchangeHeap)      \
     _(AsmJSAtomicBinopHeap)         \
     _(AsmJSAtomicBinopHeapForEffect)\
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -6824,10 +6824,19 @@ SetStopwatchCommitCallback(JSContext*, S
 
 typedef bool
 (*GetGroupsCallback)(JSContext*, PerformanceGroupVector&, void*);
 extern JS_PUBLIC_API(bool)
 SetGetPerformanceGroupsCallback(JSContext*, GetGroupsCallback, void*);
 
 } /* namespace js */
 
+namespace js {
+
+enum class CompletionKind {
+    Normal,
+    Return,
+    Throw
+};
+
+} /* namespace js */
 
 #endif /* jsapi_h */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -943,17 +943,19 @@ ArraySpeciesCreate(JSContext* cx, Handle
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 
 static bool
 array_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
+
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!args.thisv().isObject()) {
         ReportIncompatible(cx, args);
         return false;
     }
 
     Rooted<JSObject*> obj(cx, &args.thisv().toObject());
@@ -1156,17 +1158,18 @@ ArrayJoinKernel(JSContext* cx, Separator
     return true;
 }
 
 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
 // 22.1.3.13 Array.prototype.join ( separator )
 bool
 js::array_join(JSContext* cx, unsigned argc, Value* vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
@@ -1259,17 +1262,18 @@ js::array_join(JSContext* cx, unsigned a
 
 // ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f
 // 22.1.3.27 Array.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
 // ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276
 // 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]])
 static bool
 array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -267,17 +267,18 @@ assertSameCompartment(JSContext* cx,
 }
 
 #undef START_ASSERT_SAME_COMPARTMENT
 
 STATIC_PRECONDITION_ASSUME(ubound(args.argv_) >= argc)
 MOZ_ALWAYS_INLINE bool
 CallJSNative(JSContext* cx, Native native, const CallArgs& args)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
 #ifdef DEBUG
     bool alreadyThrowing = cx->isExceptionPending();
 #endif
     assertSameCompartment(cx, args);
     bool ok = native(cx, args.length(), args.base());
     if (ok) {
         assertSameCompartment(cx, args.rval());
@@ -341,50 +342,54 @@ CallJSNativeConstructor(JSContext* cx, N
 
     return true;
 }
 
 MOZ_ALWAYS_INLINE bool
 CallJSGetterOp(JSContext* cx, GetterOp op, HandleObject obj, HandleId id,
                MutableHandleValue vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     assertSameCompartment(cx, obj, id, vp);
     bool ok = op(cx, obj, id, vp);
     if (ok)
         assertSameCompartment(cx, vp);
     return ok;
 }
 
 MOZ_ALWAYS_INLINE bool
 CallJSSetterOp(JSContext* cx, SetterOp op, HandleObject obj, HandleId id, MutableHandleValue vp,
                ObjectOpResult& result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     assertSameCompartment(cx, obj, id, vp);
     return op(cx, obj, id, vp, result);
 }
 
 inline bool
 CallJSAddPropertyOp(JSContext* cx, JSAddPropertyOp op, HandleObject obj, HandleId id,
                     HandleValue v)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     assertSameCompartment(cx, obj, id, v);
     return op(cx, obj, id, v);
 }
 
 inline bool
 CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op, HandleObject receiver, HandleId id,
                        ObjectOpResult& result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     assertSameCompartment(cx, receiver, id);
     if (op)
         return op(cx, receiver, id, result);
     return result.succeed();
 }
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -396,17 +396,18 @@ JSCompartment::getNonWrapperObjectForCur
 
     // Invoke the prewrap callback. The prewrap callback is responsible for
     // doing similar reification as above, but can account for any additional
     // embedder requirements.
     //
     // We're a bit worried about infinite recursion here, so we do a check -
     // see bug 809295.
     auto preWrap = cx->runtime()->wrapObjectCallbacks->preWrap;
-    JS_CHECK_SYSTEM_RECURSION(cx, return false);
+    if (!CheckSystemRecursionLimit(cx))
+        return false;
     if (preWrap) {
         preWrap(cx, cx->global(), obj, objectPassedToWrap, obj);
         if (!obj)
             return false;
     }
     MOZ_ASSERT(!IsWindow(obj));
 
     return true;
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -489,17 +489,18 @@ Error(JSContext* cx, unsigned argc, Valu
 
 #if JS_HAS_TOSOURCE
 /*
  * Return a string that may eval to something similar to the original object.
  */
 static bool
 exn_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     RootedValue nameVal(cx);
     RootedString name(cx);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -959,95 +959,117 @@ IsObjectInContextCompartment(JSObject* o
 #define JSITER_OWNONLY    0x8   /* iterate over obj's own properties only */
 #define JSITER_HIDDEN     0x10  /* also enumerate non-enumerable properties */
 #define JSITER_SYMBOLS    0x20  /* also include symbol property keys */
 #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */
 
 JS_FRIEND_API(bool)
 RunningWithTrustedPrincipals(JSContext* cx);
 
-inline uintptr_t
+MOZ_ALWAYS_INLINE uintptr_t
 GetNativeStackLimit(JSContext* cx, JS::StackKind kind, int extraAllowance = 0)
 {
     uintptr_t limit = JS::RootingContext::get(cx)->nativeStackLimit[kind];
 #if JS_STACK_GROWTH_DIRECTION > 0
     limit += extraAllowance;
 #else
     limit -= extraAllowance;
 #endif
     return limit;
 }
 
-inline uintptr_t
+MOZ_ALWAYS_INLINE uintptr_t
 GetNativeStackLimit(JSContext* cx, int extraAllowance = 0)
 {
     JS::StackKind kind = RunningWithTrustedPrincipals(cx) ? JS::StackForTrustedScript
                                                           : JS::StackForUntrustedScript;
     return GetNativeStackLimit(cx, kind, extraAllowance);
 }
 
 /*
- * These macros report a stack overflow and run |onerror| if we are close to
- * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a
- * little extra space so that we can ensure that crucial code is able to run.
- * JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check,
- * including a safety buffer (as in, it uses the untrusted limit and subtracts
- * a little more from it).
+ * These functions return |false| if we are close to using up the C++ stack.
+ * They also report an overrecursion error, except for the DontReport variants.
+ * The CheckSystemRecursionLimit variant gives us a little extra space so we
+ * can ensure that crucial code is able to run. CheckRecursionLimitConservative
+ * allows less space than any other check, including a safety buffer (as in, it
+ * uses the untrusted limit and subtracts a little more from it).
  */
 
-#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror)                            \
-    JS_BEGIN_MACRO                                                              \
-        int stackDummy_;                                                        \
-        if (!JS_CHECK_STACK_SIZE(limit, &stackDummy_)) {                        \
-            js::ReportOverRecursed(cx);                                         \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
-#define JS_CHECK_RECURSION(cx, onerror)                                         \
-    JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx), onerror)
-
-#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror)                \
-    JS_BEGIN_MACRO                                                              \
-        int stackDummy_;                                                        \
-        if (!JS_CHECK_STACK_SIZE(limit, &stackDummy_)) {                        \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
-#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror)                             \
-    JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, js::GetNativeStackLimit(cx), onerror)
-
-#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror)                 \
-    JS_BEGIN_MACRO                                                              \
-        if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp)) {            \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
-#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror)                             \
-    JS_BEGIN_MACRO                                                              \
-        if (!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp)) {            \
-            js::ReportOverRecursed(cx);                                         \
-            onerror;                                                            \
-        }                                                                       \
-    JS_END_MACRO
-
-#define JS_CHECK_SYSTEM_RECURSION(cx, onerror)                                  \
-    JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, JS::StackForSystemCode), onerror)
-
-#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror)                            \
-    JS_CHECK_RECURSION_LIMIT(cx,                                                \
-                             js::GetNativeStackLimit(cx, JS::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \
-                             onerror)
-
-#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror)                \
-    JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx,                                    \
-                                         js::GetNativeStackLimit(cx, JS::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \
-                                         onerror)
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimit(JSContext* cx, uintptr_t limit)
+{
+    int stackDummy;
+    if (!JS_CHECK_STACK_SIZE(limit, &stackDummy)) {
+        ReportOverRecursed(cx);
+        return false;
+    }
+    return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitDontReport(JSContext* cx, uintptr_t limit)
+{
+    int stackDummy;
+    return JS_CHECK_STACK_SIZE(limit, &stackDummy);
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimit(JSContext* cx)
+{
+    // GetNativeStackLimit(cx) is pretty slow because it has to do an uninlined
+    // call to RunningWithTrustedPrincipals to determine which stack limit to
+    // use. To work around this, check the untrusted limit first to avoid the
+    // overhead in most cases.
+    uintptr_t untrustedLimit = GetNativeStackLimit(cx, JS::StackForUntrustedScript);
+    if (MOZ_LIKELY(CheckRecursionLimitDontReport(cx, untrustedLimit)))
+        return true;
+    return CheckRecursionLimit(cx, GetNativeStackLimit(cx));
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitDontReport(JSContext* cx)
+{
+    return CheckRecursionLimitDontReport(cx, GetNativeStackLimit(cx));
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitWithStackPointerDontReport(JSContext* cx, void* sp)
+{
+    return JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), sp);
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitWithStackPointer(JSContext* cx, void* sp)
+{
+    if (!JS_CHECK_STACK_SIZE(GetNativeStackLimit(cx), sp)) {
+        ReportOverRecursed(cx);
+        return false;
+    }
+    return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckSystemRecursionLimit(JSContext* cx)
+{
+    return CheckRecursionLimit(cx, GetNativeStackLimit(cx, JS::StackForSystemCode));
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitConservative(JSContext* cx)
+{
+    return CheckRecursionLimit(cx, GetNativeStackLimit(cx, JS::StackForUntrustedScript,
+                                                       -1024 * int(sizeof(size_t))));
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitConservativeDontReport(JSContext* cx)
+{
+    return CheckRecursionLimitDontReport(cx, GetNativeStackLimit(cx, JS::StackForUntrustedScript,
+                                                                 -1024 * int(sizeof(size_t))));
+}
 
 JS_FRIEND_API(void)
 StartPCCountProfiling(JSContext* cx);
 
 JS_FRIEND_API(void)
 StopPCCountProfiling(JSContext* cx);
 
 JS_FRIEND_API(void)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -467,17 +467,18 @@ js::GetPropertyKeys(JSContext* cx, Handl
                     props);
 }
 
 size_t sCustomIteratorCount = 0;
 
 static inline bool
 GetCustomIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleObject objp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     RootedValue rval(cx);
     /* Check whether we have a valid __iterator__ method. */
     HandlePropertyName name = cx->names().iteratorIntrinsic;
     if (!GetProperty(cx, obj, obj, name, &rval))
         return false;
 
     /* If there is no custom __iterator__ method, we are done here. */
@@ -1437,17 +1438,18 @@ js::IteratorMore(JSContext* cx, HandleOb
             return false;
 
         if (done)
             rval.setMagic(JS_NO_ITER_VALUE);
         return true;
     }
 
     // We're reentering below and can call anything.
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     // Call the iterator object's .next method.
     if (!GetProperty(cx, iterobj, iterobj, cx->names().next, rval))
         return false;
 
     // Call the .next method.  Fall through to the error-handling cases in the
     // unlikely event that either one of the fallible operations performed
     // during the call process fails.
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -526,17 +526,18 @@ JA(JSContext* cx, HandleObject obj, Stri
 }
 
 static bool
 Str(JSContext* cx, const Value& v, StringifyContext* scx)
 {
     /* Step 11 must be handled by the caller. */
     MOZ_ASSERT(!IsFilteredValue(v));
 
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     /*
      * This method implements the Str algorithm in ES5 15.12.3, but:
      *
      *   * We move property retrieval (step 1) into callers to stream the
      *     stringification process and avoid constantly copying strings.
      *   * We move the preprocessing in steps 2-4 into a helper function to
      *     allow both JO and JA to use this method.  While JA could use it
@@ -754,17 +755,18 @@ js::Stringify(JSContext* cx, MutableHand
 
     return Str(cx, vp, &scx);
 }
 
 /* ES5 15.12.2 Walk. */
 static bool
 Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, MutableHandleValue vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     /* Step 1. */
     RootedValue val(cx);
     if (!GetProperty(cx, holder, holder, name, &val))
         return false;
 
     /* Step 2. */
     if (val.isObject()) {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -80,17 +80,16 @@ CopyScript(JSContext* cx, HandleScript s
  * heuristics that need to know whether a given op is inside a loop.
  */
 enum JSTryNoteKind {
     JSTRY_CATCH,
     JSTRY_FINALLY,
     JSTRY_FOR_IN,
     JSTRY_FOR_OF,
     JSTRY_LOOP,
-    JSTRY_ITERCLOSE,
     JSTRY_DESTRUCTURING_ITERCLOSE
 };
 
 /*
  * Exception handling record.
  */
 struct JSTryNote {
     uint8_t         kind;       /* one of JSTryNoteKind */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -449,17 +449,18 @@ const Class StringObject::class_ = {
 
 /*
  * Perform the initial |RequireObjectCoercible(thisv)| and |ToString(thisv)|
  * from nearly all String.prototype.* functions.
  */
 static MOZ_ALWAYS_INLINE JSString*
 ToStringForStringFunction(JSContext* cx, HandleValue thisv)
 {
-    JS_CHECK_RECURSION(cx, return nullptr);
+    if (!CheckRecursionLimit(cx))
+        return nullptr;
 
     if (thisv.isString())
         return thisv.toString();
 
     if (thisv.isObject()) {
         RootedObject obj(cx, &thisv.toObject());
         if (obj->is<StringObject>()) {
             StringObject* nobj = &obj->as<StringObject>();
@@ -3140,17 +3141,18 @@ SymbolToSource(JSContext* cx, Symbol* sy
     if (!buf.append(')'))
         return nullptr;
     return buf.finishString();
 }
 
 JSString*
 js::ValueToSource(JSContext* cx, HandleValue v)
 {
-    JS_CHECK_RECURSION(cx, return nullptr);
+    if (!CheckRecursionLimit(cx))
+        return nullptr;
     assertSameCompartment(cx, v);
 
     if (v.isUndefined())
         return cx->names().void0;
     if (v.isString())
         return StringToSource(cx, v.toString());
     if (v.isSymbol())
         return SymbolToSource(cx, v.toSymbol());
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -87,17 +87,19 @@ js::assertEnteredPolicy(JSContext* cx, J
     MOZ_ASSERT(cx->enteredPolicy->enteredAction & act);
 }
 #endif
 
 bool
 Proxy::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                              MutableHandle<PropertyDescriptor> desc)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
+
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     desc.object().set(nullptr); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
     if (!policy.allowed())
         return policy.returnValue();
 
     // Special case. See the comment on BaseProxyHandler::mHasPrototype.
     if (handler->hasPrototype())
@@ -105,56 +107,59 @@ Proxy::getPropertyDescriptor(JSContext* 
 
     return handler->getPropertyDescriptor(cx, proxy, id, desc);
 }
 
 bool
 Proxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                 MutableHandle<PropertyDescriptor> desc)
 {
-    JS_CHECK_RECURSION(cx, return false);
-
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     desc.object().set(nullptr); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
 }
 
 bool
 Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
                       Handle<PropertyDescriptor> desc, ObjectOpResult& result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed()) {
         if (!policy.returnValue())
             return false;
         return result.succeed();
     }
     return proxy->as<ProxyObject>().handler()->defineProperty(cx, proxy, id, desc, result);
 }
 
 bool
 Proxy::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     if (!policy.allowed())
         return policy.returnValue();
     return proxy->as<ProxyObject>().handler()->ownPropertyKeys(cx, proxy, props);
 }
 
 bool
 Proxy::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed()) {
         bool ok = policy.returnValue();
         if (ok)
             result.succeed();
         return ok;
     }
@@ -182,64 +187,71 @@ js::AppendUnique(JSContext* cx, AutoIdVe
     }
     return base.appendAll(uniqueOthers);
 }
 
 /* static */ bool
 Proxy::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject proto)
 {
     MOZ_ASSERT(proxy->hasDynamicPrototype());
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->getPrototype(cx, proxy, proto);
 }
 
 /* static */ bool
 Proxy::setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto, ObjectOpResult& result)
 {
     MOZ_ASSERT(proxy->hasDynamicPrototype());
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->setPrototype(cx, proxy, proto, result);
 }
 
 /* static */ bool
 Proxy::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
                               MutableHandleObject proto)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->getPrototypeIfOrdinary(cx, proxy, isOrdinary,
                                                                       proto);
 }
 
 /* static */ bool
 Proxy::setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     return handler->setImmutablePrototype(cx, proxy, succeeded);
 }
 
 /* static */ bool
 Proxy::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     return handler->preventExtensions(cx, proxy, result);
 }
 
 /* static */ bool
 Proxy::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->isExtensible(cx, proxy, extensible);
 }
 
 bool
 Proxy::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
 
     if (handler->hasPrototype()) {
         if (!handler->hasOwn(cx, proxy, id, bp))
@@ -257,17 +269,18 @@ Proxy::has(JSContext* cx, HandleObject p
     }
 
     return handler->has(cx, proxy, id, bp);
 }
 
 bool
 Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->hasOwn(cx, proxy, id, bp);
 }
 
@@ -278,17 +291,18 @@ ValueToWindowProxyIfWindow(const Value& 
         return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
     return v;
 }
 
 MOZ_ALWAYS_INLINE bool
 Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id,
            MutableHandleValue vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     vp.setUndefined(); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
 
     // Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
     // shouldn't have to know about the Window/WindowProxy distinction.
@@ -329,17 +343,18 @@ js::ProxyGetPropertyByValue(JSContext* c
     RootedValue receiver(cx, ObjectValue(*proxy));
     return Proxy::get(cx, proxy, receiver, id, vp);
 }
 
 bool
 Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver_,
            ObjectOpResult& result)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed()) {
         if (!policy.returnValue())
             return false;
         return result.succeed();
     }
 
@@ -377,28 +392,30 @@ js::ProxySetPropertyByValue(JSContext* c
     if (!Proxy::set(cx, proxy, id, val, receiver, result))
         return false;
     return result.checkStrictErrorOrWarning(cx, proxy, id, strict);
 }
 
 bool
 Proxy::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::ENUMERATE, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->getOwnEnumerablePropertyKeys(cx, proxy, props);
 }
 
 bool
 Proxy::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     objp.set(nullptr); // default result if we refuse to perform this action
 
     if (handler->hasPrototype()) {
         AutoIdVector props(cx);
         if (!Proxy::getOwnEnumerablePropertyKeys(cx, proxy, props))
             return false;
 
@@ -425,17 +442,18 @@ Proxy::enumerate(JSContext* cx, HandleOb
             NewEmptyPropertyIterator(cx, 0, objp);
     }
     return handler->enumerate(cx, proxy, objp);
 }
 
 bool
 Proxy::call(JSContext* cx, HandleObject proxy, const CallArgs& args)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
 
     // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
     // can only set our default value once we're sure that we're not calling the
     // trap.
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
                            BaseProxyHandler::CALL, true);
     if (!policy.allowed()) {
@@ -444,17 +462,18 @@ Proxy::call(JSContext* cx, HandleObject 
     }
 
     return handler->call(cx, proxy, args);
 }
 
 bool
 Proxy::construct(JSContext* cx, HandleObject proxy, const CallArgs& args)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
 
     // Because vp[0] is JS_CALLEE on the way in and JS_RVAL on the way out, we
     // can only set our default value once we're sure that we're not calling the
     // trap.
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
                            BaseProxyHandler::CALL, true);
     if (!policy.allowed()) {
@@ -463,47 +482,51 @@ Proxy::construct(JSContext* cx, HandleOb
     }
 
     return handler->construct(cx, proxy, args);
 }
 
 bool
 Proxy::nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl, const CallArgs& args)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     RootedObject proxy(cx, &args.thisv().toObject());
     // Note - we don't enter a policy here because our security architecture
     // guards against nativeCall by overriding the trap itself in the right
     // circumstances.
     return proxy->as<ProxyObject>().handler()->nativeCall(cx, test, impl, args);
 }
 
 bool
 Proxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
 }
 
 bool
 Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, cls);
 }
 
 bool
 Proxy::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
 }
 
 const char*
 Proxy::className(JSContext* cx, HandleObject proxy)
 {
     // Check for unbounded recursion, but don't signal an error; className
     // needs to be infallible.
@@ -519,61 +542,67 @@ Proxy::className(JSContext* cx, HandleOb
         return handler->BaseProxyHandler::className(cx, proxy);
     }
     return handler->className(cx, proxy);
 }
 
 JSString*
 Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
 {
-    JS_CHECK_RECURSION(cx, return nullptr);
+    if (!CheckRecursionLimit(cx))
+        return nullptr;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE,
                            BaseProxyHandler::GET, /* mayThrow = */ false);
     // Do the safe thing if the policy rejects.
     if (!policy.allowed())
         return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
     return handler->fun_toString(cx, proxy, indent);
 }
 
 bool
 Proxy::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->regexp_toShared(cx, proxy, g);
 }
 
 bool
 Proxy::boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->boxedValue_unbox(cx, proxy, vp);
 }
 
 JSObject * const TaggedProto::LazyProto = reinterpret_cast<JSObject*>(0x1);
 
 /* static */ bool
 Proxy::watch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleObject callable)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->watch(cx, proxy, id, callable);
 }
 
 /* static */ bool
 Proxy::unwatch(JSContext* cx, JS::HandleObject proxy, JS::HandleId id)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     return proxy->as<ProxyObject>().handler()->unwatch(cx, proxy, id);
 }
 
 /* static */ bool
 Proxy::getElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_t end,
                    ElementAdder* adder)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET,
                            /* mayThrow = */ true);
     if (!policy.allowed()) {
         if (policy.returnValue()) {
             MOZ_ASSERT(!cx->isExceptionPending());
             return js::GetElementsWithAdder(cx, proxy, proxy, begin, end, adder);
         }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2680,18 +2680,16 @@ TryNoteName(JSTryNoteKind kind)
       case JSTRY_FINALLY:
         return "finally";
       case JSTRY_FOR_IN:
         return "for-in";
       case JSTRY_FOR_OF:
         return "for-of";
       case JSTRY_LOOP:
         return "loop";
-      case JSTRY_ITERCLOSE:
-        return "iterclose";
       case JSTRY_DESTRUCTURING_ITERCLOSE:
         return "dstr-iterclose";
     }
 
     MOZ_CRASH("Bad JSTryNoteKind");
 }
 
 static MOZ_MUST_USE bool
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2747,17 +2747,18 @@ DebugEnvironments::onCompartmentUnsetIsD
         envs->missingEnvs.clear();
         envs->liveEnvs.clear();
     }
 }
 
 bool
 DebugEnvironments::updateLiveEnvironments(JSContext* cx)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     /*
      * Note that we must always update the top frame's environment objects'
      * entries in liveEnvs because we can't be sure code hasn't run in that
      * frame to change the environment chain since we were last called. The
      * fp->prevUpToDate() flag indicates whether the environments of frames
      * older than fp are already included in liveEnvs. It might seem simpler
      * to have fp instead carry a flag indicating whether fp itself is
@@ -2988,17 +2989,18 @@ GetDebugEnvironmentForNonEnvironmentObje
         MOZ_ASSERT(!o->is<EnvironmentObject>());
 #endif
     return &enclosing;
 }
 
 static JSObject*
 GetDebugEnvironment(JSContext* cx, const EnvironmentIter& ei)
 {
-    JS_CHECK_RECURSION(cx, return nullptr);
+    if (!CheckRecursionLimit(cx))
+        return nullptr;
 
     if (ei.done())
         return GetDebugEnvironmentForNonEnvironmentObject(ei);
 
     if (ei.hasAnyEnvironmentObject())
         return GetDebugEnvironmentForEnvironmentObject(cx, ei);
 
     if (ei.scope().is<FunctionScope>() ||
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -338,17 +338,18 @@ ExecuteState::pushInterpreterFrame(JSCon
 // stack frames and stack overflow issues, see bug 1167883. Turn off PGO to
 // avoid this.
 #ifdef _MSC_VER
 # pragma optimize("g", off)
 #endif
 bool
 js::RunScript(JSContext* cx, RunState& state)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     // Since any script can conceivably GC, make sure it's safe to do so.
     cx->verifyIsSafeToGC();
 
     MOZ_DIAGNOSTIC_ASSERT(cx->compartment()->isSystem() ||
                           cx->runtime()->allowContentJS());
 
     MOZ_ASSERT(!cx->enableAccessValidation ||
@@ -613,27 +614,29 @@ js::InternalConstructWithProvidedThis(JS
     return true;
 }
 
 bool
 js::CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter, MutableHandleValue rval)
 {
     // Invoke could result in another try to get or set the same id again, see
     // bug 355497.
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     FixedInvokeArgs<0> args(cx);
 
     return Call(cx, getter, thisv, args, rval);
 }
 
 bool
 js::CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter, HandleValue v)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
 
     FixedInvokeArgs<1> args(cx);
 
     args[0].set(v);
 
     RootedValue ignored(cx);
     return Call(cx, setter, thisv, args, &ignored);
 }
@@ -1177,27 +1180,16 @@ ProcessTryNotes(JSContext* cx, Environme
                 // location of the throw (the iterator). Indeed, we must
                 // settle to avoid infinitely handling the same exception.
                 SettleOnTryNote(cx, tn, ei, regs);
                 return ErrorReturnContinuation;
             }
             break;
           }
 
-          case JSTRY_ITERCLOSE: {
-            // The iterator object is at the top of the stack.
-            Value* sp = regs.spForStackDepth(tn->stackDepth);
-            RootedObject iterObject(cx, &sp[-1].toObject());
-            if (!IteratorCloseForException(cx, iterObject)) {
-                SettleOnTryNote(cx, tn, ei, regs);
-                return ErrorReturnContinuation;
-            }
-            break;
-          }
-
           case JSTRY_DESTRUCTURING_ITERCLOSE: {
             // Whether the destructuring iterator is done is at the top of the
             // stack. The iterator object is second from the top.
             MOZ_ASSERT(tn->stackDepth > 1);
             Value* sp = regs.spForStackDepth(tn->stackDepth);
             RootedValue doneValue(cx, sp[-1]);
             bool done = ToBoolean(doneValue);
             if (!done) {
@@ -1876,17 +1868,16 @@ CASE(EnableInterruptsPseudoOpcode)
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_NOP_DESTRUCTURING)
 CASE(JSOP_UNUSED192)
 CASE(JSOP_UNUSED209)
 CASE(JSOP_UNUSED210)
 CASE(JSOP_UNUSED211)
-CASE(JSOP_UNUSED219)
 CASE(JSOP_UNUSED220)
 CASE(JSOP_UNUSED221)
 CASE(JSOP_UNUSED222)
 CASE(JSOP_UNUSED223)
 CASE(JSOP_CONDSWITCH)
 {
     MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1);
     ADVANCE_AND_DISPATCH(1);
@@ -2622,16 +2613,25 @@ CASE(JSOP_CHECKISOBJ)
 {
     if (!REGS.sp[-1].isObject()) {
         MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, CheckIsObjectKind(GET_UINT8(REGS.pc))));
         goto error;
     }
 }
 END_CASE(JSOP_CHECKISOBJ)
 
+CASE(JSOP_CHECKISCALLABLE)
+{
+    if (!IsCallable(REGS.sp[-1])) {
+        MOZ_ALWAYS_FALSE(ThrowCheckIsCallable(cx, CheckIsCallableKind(GET_UINT8(REGS.pc))));
+        goto error;
+    }
+}
+END_CASE(JSOP_CHECKISCALLABLE)
+
 CASE(JSOP_CHECKTHIS)
 {
     if (REGS.sp[-1].isMagic(JS_UNINITIALIZED_LEXICAL)) {
         MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx, REGS.fp()));
         goto error;
     }
 }
 END_CASE(JSOP_CHECKTHIS)
@@ -5036,16 +5036,29 @@ js::ThrowCheckIsObject(JSContext* cx, Ch
         break;
       default:
         MOZ_CRASH("Unknown kind");
     }
     return false;
 }
 
 bool
+js::ThrowCheckIsCallable(JSContext* cx, CheckIsCallableKind kind)
+{
+    switch (kind) {
+      case CheckIsCallableKind::IteratorReturn:
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_RETURN_NOT_CALLABLE);
+        break;
+      default:
+        MOZ_CRASH("Unknown kind");
+    }
+    return false;
+}
+
+bool
 js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame)
 {
     RootedFunction fun(cx);
     if (frame.isFunctionFrame()) {
         fun = frame.callee();
     } else {
         Scope* startingScope;
         if (frame.isDebuggerEvalFrame()) {
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -557,16 +557,23 @@ enum class CheckIsObjectKind : uint8_t {
     IteratorReturn,
     IteratorThrow,
     GetIterator
 };
 
 bool
 ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
 
+enum class CheckIsCallableKind : uint8_t {
+    IteratorReturn
+};
+
+bool
+ThrowCheckIsCallable(JSContext* cx, CheckIsCallableKind kind);
+
 bool
 ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);
 
 bool
 DefaultClassConstructor(JSContext* cx, unsigned argc, Value* vp);
 
 bool
 Debug_CheckSelfHosted(JSContext* cx, HandleValue v);
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2015,17 +2015,18 @@ GetNonexistentProperty(JSContext* cx, Na
 {
     return false;
 }
 
 static inline bool
 GeneralizedGetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver,
                        IsNameLookup nameLookup, MutableHandleValue vp)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!CheckRecursionLimit(cx))
+        return false;
     if (nameLookup) {
         // When nameLookup is true, GetProperty implements ES6 rev 34 (2015 Feb
         // 20) 8.1.1.2.6 GetBindingValue, with step 3 (the call to HasProperty)
         // and step 6 (the call to Get) fused so that only a single lookup is
         // needed.
         //
         // If we get here, we've reached a non-native object. Fall back on the
         // algorithm as specified, with two separate lookups. (Note that we
@@ -2040,17 +2041,18 @@ GeneralizedGetProperty(JSContext* cx, Ha
 
     return GetProperty(cx, obj, receiver, id, vp);
 }
 
 static inline bool
 GeneralizedGetProperty(JSContext* cx, JSObject* obj, jsid id, const Value& receiver,
                        IsNameLookup nameLookup, FakeMutableHandle<Value> vp)
 {
-    JS_CHECK_RECURSION_DONT_REPORT(cx, return false);
+    if (!CheckRecursionLimitDontReport(cx))
+        return false;
     if (nameLookup)
         return false;
     return GetPropertyNoGC(cx, obj, receiver, id, vp.address());
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 NativeGetPropertyInline(JSContext* cx,
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2202,17 +2202,26 @@ 1234567890123456789012345678901234567890
      * This opcode is used with the JSOP_NEWARRAY opcode.
      *   Category: Literals
      *   Type: Array
      *   Operands:
      *   Stack: => hole
      */ \
     macro(JSOP_HOLE,          218, "hole",         NULL,  1,  0,  1,  JOF_BYTE) \
     \
-    macro(JSOP_UNUSED219,     219,"unused219",     NULL,  1,  0,  0,  JOF_BYTE) \
+    /*
+     * Checks that the top value on the stack is callable, and throws a
+     * TypeError if not. The operand 'kind' is used only to generate an
+     * appropriate error message.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint8_t kind
+     *   Stack: result => result, callable
+     */ \
+    macro(JSOP_CHECKISCALLABLE, 219, "checkiscallable", NULL, 2, 1, 1, JOF_UINT8) \
     macro(JSOP_UNUSED220,     220,"unused220",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED221,     221,"unused221",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED222,     222,"unused222",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED223,     223,"unused223",     NULL,  1,  0,  0,  JOF_BYTE) \
     \
     /*
      * Creates rest parameter array for current function call, and pushes it
      * onto the stack.
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -5798,17 +5798,18 @@ CheckCoercedAtomicsBuiltinCall(FunctionV
     return CoerceResult(f, callNode, ret, actual, type);
 }
 
 static bool
 CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
 {
     MOZ_ASSERT(ret.isCanonical());
 
-    JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
+    if (!CheckRecursionLimitDontReport(f.cx()))
+        return f.m().failOverRecursed();
 
     bool isSimd = false;
     if (IsNumericLiteral(f.m(), call, &isSimd)) {
         if (isSimd)
             f.setUsesSimd();
         NumLit lit = ExtractNumericLiteral(f.m(), call);
         if (!f.writeConstExpr(lit))
             return false;
@@ -6107,17 +6108,18 @@ CheckMultiply(FunctionValidator& f, Pars
     }
 
     return f.fail(star, "multiply operands must be both int, both double? or both float?");
 }
 
 static bool
 CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAddOrSubOut = nullptr)
 {
-    JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
+    if (!CheckRecursionLimitDontReport(f.cx()))
+        return f.m().failOverRecursed();
 
     MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
     ParseNode* lhs = AddSubLeft(expr);
     ParseNode* rhs = AddSubRight(expr);
 
     Type lhsType, rhsType;
     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
 
@@ -6347,17 +6349,18 @@ CheckBitwise(FunctionValidator& f, Parse
     }
 
     return true;
 }
 
 static bool
 CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
 {
-    JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
+    if (!CheckRecursionLimitDontReport(f.cx()))
+        return f.m().failOverRecursed();
 
     bool isSimd = false;
     if (IsNumericLiteral(f.m(), expr, &isSimd)) {
         if (isSimd)
             f.setUsesSimd();
         return CheckNumericLiteral(f, expr, type);
     }
 
@@ -7006,17 +7009,18 @@ CheckBreakOrContinue(FunctionValidator& 
     if (PropertyName* maybeLabel = LoopControlMaybeLabel(stmt))
         return f.writeLabeledBreakOrContinue(maybeLabel, isBreak);
     return f.writeUnlabeledBreakOrContinue(isBreak);
 }
 
 static bool
 CheckStatement(FunctionValidator& f, ParseNode* stmt)
 {
-    JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
+    if (!CheckRecursionLimitDontReport(f.cx()))
+        return f.m().failOverRecursed();
 
     switch (stmt->getKind()) {
       case PNK_SEMI:          return CheckExprStatement(f, stmt);
       case PNK_WHILE:         return CheckWhile(f, stmt);
       case PNK_FOR:           return CheckFor(f, stmt);
       case PNK_DOWHILE:       return CheckDoWhile(f, stmt);
       case PNK_LABEL:         return CheckLabel(f, stmt);
       case PNK_IF:            return CheckIf(f, stmt);
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -253,17 +253,18 @@ XPCArrayHomogenizer::GetTypeForArray(JSC
             NS_ERROR("bad state");
             return false;
     }
     return true;
 }
 
 bool XPCVariant::InitializeData(JSContext* cx)
 {
-    JS_CHECK_RECURSION(cx, return false);
+    if (!js::CheckRecursionLimit(cx))
+        return false;
 
     RootedValue val(cx, GetJSVal());
 
     if (val.isInt32()) {
         mData.SetFromInt32(val.toInt32());
         return true;
     }
     if (val.isDouble()) {
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -2908,21 +2908,21 @@ PresShell::RecreateFramesFor(nsIContent*
   nsAutoScriptBlocker scriptBlocker;
 
   nsStyleChangeList changeList;
   changeList.AppendChange(nullptr, aContent, nsChangeHint_ReconstructFrame);
 
   // Mark ourselves as not safe to flush while we're doing frame construction.
   ++mChangeNestCount;
   RestyleManager* restyleManager = mPresContext->RestyleManager();
-  nsresult rv = restyleManager->ProcessRestyledFrames(changeList);
+  restyleManager->ProcessRestyledFrames(changeList);
   restyleManager->FlushOverflowChangedTracker();
   --mChangeNestCount;
 
-  return rv;
+  return NS_OK;
 }
 
 void
 nsIPresShell::PostRecreateFramesFor(Element* aElement)
 {
   mPresContext->RestyleManager()->PostRestyleEvent(aElement, nsRestyleHint(0),
                                                    nsChangeHint_ReconstructFrame);
 }
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1355,23 +1355,23 @@ RestyleManager::GetNextContinuationWithS
     nextContinuation = nullptr;
     if (aHaveMoreContinuations) {
       *aHaveMoreContinuations = true;
     }
   }
   return nextContinuation;
 }
 
-nsresult
+void
 RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                "Someone forgot a script blocker");
   if (aChangeList.IsEmpty())
-    return NS_OK;
+    return;
 
   PROFILER_LABEL("RestyleManager", "ProcessRestyledFrames",
                  js::ProfileEntry::Category::CSS);
 
   nsPresContext* presContext = PresContext();
   FramePropertyTable* propTable = presContext->PropertyTable();
   nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
 
@@ -1672,17 +1672,16 @@ RestyleManager::ProcessRestyledFrames(ns
                data.mFrame->GetType() != nsGkAtoms::viewportFrame) {
       NS_WARNING("Unable to test style tree integrity -- no content node "
                  "(and not a viewport frame)");
     }
 #endif
   }
 
   aChangeList.Clear();
-  return NS_OK;
 }
 
 RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
                                                 RestyleManager* aRestyleManager)
   : mRestyleManager(aRestyleManager)
   , mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
 {
   MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -73,17 +73,17 @@ public:
     mOverflowChangedTracker.RemoveFrame(aFrame);
   }
 
   // Note: It's the caller's responsibility to make sure to wrap a
   // ProcessRestyledFrames call in a view update batch and a script blocker.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
-  nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
+  void ProcessRestyledFrames(nsStyleChangeList& aChangeList);
 
   bool IsInStyleRefresh() const { return mInStyleRefresh; }
 
   // AnimationsWithDestroyedFrame is used to stop animations and transitions
   // on elements that have no frame at the end of the restyling process.
   // It only lives during the restyling process.
   class MOZ_STACK_CLASS AnimationsWithDestroyedFrame final {
   public:
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/ServoRestyleManager.h"
 
 #include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "nsContentUtils.h"
+#include "nsCSSFrameConstructor.h"
 #include "nsPrintfCString.h"
 #include "nsRefreshDriver.h"
 #include "nsStyleChangeList.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
@@ -191,34 +192,47 @@ ServoRestyleManager::RecreateStyleContex
   //
   // Hold the old style context alive, because it could become a dangling
   // pointer during the replacement. In practice it's not a huge deal (on
   // GetNextContinuationWithSameStyle the pointer is not dereferenced, only
   // compared), but better not playing with dangling pointers if not needed.
   RefPtr<nsStyleContext> oldStyleContext =
     styleFrame ? styleFrame->StyleContext() : nullptr;
 
+  UndisplayedNode* displayContentsNode = nullptr;
+  // FIXME(emilio, bug 1303605): This can be simpler for Servo.
+  // Note that we intentionally don't check for display: none content.
+  if (!oldStyleContext) {
+    displayContentsNode =
+      PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement);
+    if (displayContentsNode) {
+      oldStyleContext = displayContentsNode->mStyle;
+    }
+  }
+
   RefPtr<ServoComputedValues> computedValues =
     aStyleSet->ResolveServoStyle(aElement);
 
   // Note that we rely in the fact that we don't cascade pseudo-element styles
   // separately right now (that is, if a pseudo style changes, the normal style
   // changes too).
   //
   // Otherwise we should probably encode that information somehow to avoid
   // expensive checks in the common case.
   //
   // Also, we're going to need to check for pseudos of display: contents
   // elements, though that is buggy right now even in non-stylo mode, see
   // bug 1251799.
   const bool recreateContext = oldStyleContext &&
     oldStyleContext->StyleSource().AsServoComputedValues() != computedValues;
 
+  RefPtr<nsStyleContext> newContext = nullptr;
   if (recreateContext) {
-    RefPtr<nsStyleContext> newContext =
+    MOZ_ASSERT(styleFrame || displayContentsNode);
+    newContext =
       aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr,
                             CSSPseudoElementType::NotPseudo, aElement);
 
     newContext->EnsureStructsForServo(oldStyleContext);
 
     // XXX This could not always work as expected: there are kinds of content
     // with the first split and the last sharing style, but others not. We
     // should handle those properly.
@@ -230,16 +244,21 @@ ServoRestyleManager::RecreateStyleContex
     if (isTable) {
       nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
       MOZ_ASSERT(primaryFrame->StyleContext()->GetPseudo() ==
                    nsCSSAnonBoxes::tableWrapper,
                  "What sort of frame is this?");
       UpdateStyleContextForTableWrapper(primaryFrame, newContext, aStyleSet);
     }
 
+    if (MOZ_UNLIKELY(displayContentsNode)) {
+      MOZ_ASSERT(!styleFrame);
+      displayContentsNode->mStyle = newContext;
+    }
+
     // Update pseudo-elements state if appropriate.
     const static CSSPseudoElementType pseudosToRestyle[] = {
       CSSPseudoElementType::before,
       CSSPseudoElementType::after,
     };
 
     for (CSSPseudoElementType pseudoType : pseudosToRestyle) {
       nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType);
@@ -268,34 +287,26 @@ ServoRestyleManager::RecreateStyleContex
         }
       }
     }
   }
 
   bool traverseElementChildren = aElement->HasDirtyDescendantsForServo();
   bool traverseTextChildren = recreateContext;
   if (traverseElementChildren || traverseTextChildren) {
+    nsStyleContext* upToDateContext =
+      recreateContext ? newContext : oldStyleContext;
+
     StyleChildrenIterator it(aElement);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
-        if (!styleFrame) {
-          // The frame constructor presumably decided to suppress frame
-          // construction on this subtree. Just clear the dirty descendants
-          // bit from the subtree, since there's no point in harvesting the
-          // change hints.
-          MOZ_ASSERT(!n->AsElement()->GetPrimaryFrame(),
-                     "Only display:contents should do this, and we don't handle that yet");
-          ClearDirtyDescendantsFromSubtree(n->AsElement());
-        } else {
-          RecreateStyleContexts(n->AsElement(), styleFrame->StyleContext(),
-                                aStyleSet, aChangeListToProcess);
-        }
+        RecreateStyleContexts(n->AsElement(), upToDateContext,
+                              aStyleSet, aChangeListToProcess);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
-        RecreateStyleContextsForText(n, styleFrame->StyleContext(),
-                                     aStyleSet);
+        RecreateStyleContextsForText(n, upToDateContext, aStyleSet);
       }
     }
   }
 
   aElement->UnsetHasDirtyDescendantsForServo();
 }
 
 void
@@ -322,16 +333,17 @@ ServoRestyleManager::FrameForPseudoEleme
 {
   MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement());
   nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
 
   if (!aPseudoTagOrNull) {
     return primaryFrame;
   }
 
+  // FIXME(emilio): Need to take into account display: contents pseudos!
   if (!primaryFrame) {
     return nullptr;
   }
 
   // NOTE: we probably need to special-case display: contents here. Gecko's
   // RestyleManager passes the primary frame of the parent instead.
   if (aPseudoTagOrNull == nsCSSPseudoElements::before) {
     return nsLayoutUtils::GetBeforeFrameForContent(primaryFrame, aContent);
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -201,33 +201,53 @@ nsFrameManager::ClearPlaceholderFrameMap
     auto entry = static_cast<PlaceholderMapEntry*>(iter.Get());
     entry->placeholderFrame->SetOutOfFlowFrame(nullptr);
   }
   mPlaceholderMap.Clear();
 }
 
 //----------------------------------------------------------------------
 
+static nsIContent*
+ParentForUndisplayedMap(const nsIContent* aContent)
+{
+  MOZ_ASSERT(aContent);
+
+  nsIContent* parent = aContent->GetParentElementCrossingShadowRoot();
+  MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements");
+
+  return parent;
+}
+
 /* static */ nsStyleContext*
-nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent)
+nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap,
+                                     const nsIContent* aContent)
+{
+  UndisplayedNode* node = GetUndisplayedNodeInMapFor(aMap, aContent);
+  return node ? node->mStyle.get() : nullptr;
+}
+
+/* static */ UndisplayedNode*
+nsFrameManager::GetUndisplayedNodeInMapFor(UndisplayedMap* aMap,
+                                           const nsIContent* aContent)
 {
   if (!aContent) {
     return nullptr;
   }
-  nsIContent* parent = aContent->GetParentElementCrossingShadowRoot();
-  MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements");
+  nsIContent* parent = ParentForUndisplayedMap(aContent);
   for (UndisplayedNode* node = aMap->GetFirstNode(parent);
          node; node = node->mNext) {
     if (node->mContent == aContent)
-      return node->mStyle;
+      return node;
   }
 
   return nullptr;
 }
 
+
 /* static */ UndisplayedNode*
 nsFrameManager::GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap,
                                                nsIContent* aParentContent)
 {
   return aMap ? aMap->GetFirstNode(aParentContent) : nullptr;
 }
 
 UndisplayedNode*
@@ -247,18 +267,17 @@ nsFrameManager::SetStyleContextInMap(Und
 #if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP)
   static int i = 0;
   printf("SetStyleContextInMap(%d): p=%p \n", i++, (void *)aContent);
 #endif
 
   NS_ASSERTION(!GetStyleContextInMap(aMap, aContent),
                "Already have an entry for aContent");
 
-  nsIContent* parent = aContent->GetParentElementCrossingShadowRoot();
-  MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements");
+  nsIContent* parent = ParentForUndisplayedMap(aContent);
 #ifdef DEBUG
   nsIPresShell* shell = aStyleContext->PresContext()->PresShell();
   NS_ASSERTION(parent || (shell && shell->GetDocument() &&
                           shell->GetDocument()->GetRootElement() == aContent),
                "undisplayed content must have a parent, unless it's the root "
                "element");
 #endif
   aMap->AddNodeFor(parent, aContent, aStyleContext);
--- a/layout/base/nsFrameManager.h
+++ b/layout/base/nsFrameManager.h
@@ -97,17 +97,17 @@ public:
   // Placeholder frame functions
   nsPlaceholderFrame* GetPlaceholderFrameFor(const nsIFrame* aFrame);
   void RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame);
   void UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame);
 
   void      ClearPlaceholderFrameMap();
 
   // Mapping undisplayed content
-  nsStyleContext* GetUndisplayedContent(nsIContent* aContent)
+  nsStyleContext* GetUndisplayedContent(const nsIContent* aContent)
   {
     if (!mUndisplayedMap) {
       return nullptr;
     }
     return GetStyleContextInMap(mUndisplayedMap, aContent);
   }
   mozilla::UndisplayedNode*
     GetAllUndisplayedContentIn(nsIContent* aParentContent);
@@ -122,29 +122,42 @@ public:
   void ClearUndisplayedContentIn(nsIContent* aContent,
                                  nsIContent* aParentContent);
   void ClearAllUndisplayedContentIn(nsIContent* aParentContent);
 
   // display:contents related methods:
   /**
    * Return the registered display:contents style context for aContent, if any.
    */
-  nsStyleContext* GetDisplayContentsStyleFor(nsIContent* aContent)
+  nsStyleContext* GetDisplayContentsStyleFor(const nsIContent* aContent)
   {
     if (!mDisplayContentsMap) {
       return nullptr;
     }
     return GetStyleContextInMap(mDisplayContentsMap, aContent);
   }
 
   /**
    * Return the linked list of UndisplayedNodes containing the registered
    * display:contents children of aParentContent, if any.
    */
   mozilla::UndisplayedNode* GetAllDisplayContentsIn(nsIContent* aParentContent);
+
+  /**
+   * Return the relevant undisplayed node for a given content with display:
+   * contents style.
+   */
+  mozilla::UndisplayedNode* GetDisplayContentsNodeFor(
+      const nsIContent* aContent) {
+    if (!mDisplayContentsMap) {
+      return nullptr;
+    }
+    return GetUndisplayedNodeInMapFor(mDisplayContentsMap, aContent);
+  }
+
   /**
    * Register aContent having a display:contents style context.
    */
   void SetDisplayContents(nsIContent* aContent,
                           nsStyleContext* aStyleContext);
   /**
    * Change the registered style context for aContent to aStyleContext.
    */
@@ -202,17 +215,20 @@ public:
    */
   void CaptureFrameStateFor(nsIFrame*              aFrame,
                                         nsILayoutHistoryState* aState);
 
   void RestoreFrameStateFor(nsIFrame*              aFrame,
                                         nsILayoutHistoryState* aState);
 protected:
   static nsStyleContext* GetStyleContextInMap(UndisplayedMap* aMap,
-                                              nsIContent* aContent);
+                                              const nsIContent* aContent);
+  static mozilla::UndisplayedNode*
+    GetUndisplayedNodeInMapFor(UndisplayedMap* aMap,
+                               const nsIContent* aContent);
   static mozilla::UndisplayedNode*
     GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap,
                                    nsIContent* aParentContent);
   static void SetStyleContextInMap(UndisplayedMap* aMap,
                                    nsIContent* aContent,
                                    nsStyleContext* aStyleContext);
   static void ChangeStyleContextInMap(UndisplayedMap* aMap,
                                       nsIContent* aContent,
--- a/layout/reftests/css-grid/reftest-stylo.list
+++ b/layout/reftests/css-grid/reftest-stylo.list
@@ -169,18 +169,18 @@ fails == grid-repeat-auto-fill-fit-002.h
 fails == grid-repeat-auto-fill-fit-003.html grid-repeat-auto-fill-fit-003.html
 fails == grid-repeat-auto-fill-fit-004.html grid-repeat-auto-fill-fit-004.html
 fails == grid-repeat-auto-fill-fit-005.html grid-repeat-auto-fill-fit-005.html
 fails == grid-repeat-auto-fill-fit-006.html grid-repeat-auto-fill-fit-006.html
 fails == grid-repeat-auto-fill-fit-007.html grid-repeat-auto-fill-fit-007.html
 fails == grid-repeat-auto-fill-fit-008.html grid-repeat-auto-fill-fit-008.html
 fails == grid-repeat-auto-fill-fit-009.html grid-repeat-auto-fill-fit-009.html
 fails == grid-repeat-auto-fill-fit-010.html grid-repeat-auto-fill-fit-010.html
-# == grid-repeat-auto-fill-fit-011.html grid-repeat-auto-fill-fit-011.html # bug 1342710
-fails == grid-item-blockifying-001.html grid-item-blockifying-001.html # bug 1335339
+fails == grid-repeat-auto-fill-fit-011.html grid-repeat-auto-fill-fit-011.html
+fails == grid-item-blockifying-001.html grid-item-blockifying-001.html
 fails == grid-fragmentation-001.html grid-fragmentation-001.html
 fails == grid-fragmentation-002.html grid-fragmentation-002.html
 fails == grid-fragmentation-003.html grid-fragmentation-003.html
 fails == grid-fragmentation-004.html grid-fragmentation-004.html
 fails == grid-fragmentation-005.html grid-fragmentation-005.html
 fails == grid-fragmentation-006.html grid-fragmentation-006.html
 fails == grid-fragmentation-007.html grid-fragmentation-007.html
 fails == grid-fragmentation-008.html grid-fragmentation-008.html
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -4,16 +4,17 @@
  * 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/. */
 
 #include "mozilla/ServoBindings.h"
 
 #include "ChildIterator.h"
 #include "gfxFontFamilyList.h"
 #include "nsAttrValueInlines.h"
+#include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
 #include "nsCSSParser.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRuleProcessor.h"
 #include "nsContentUtils.h"
 #include "nsDOMTokenList.h"
 #include "nsIContentInlines.h"
 #include "nsIDOMNode.h"
@@ -274,21 +275,31 @@ Gecko_SetOwnerDocumentNeedsStyleFlush(Ra
 
 nsStyleContext*
 Gecko_GetStyleContext(RawGeckoNodeBorrowed aNode, nsIAtom* aPseudoTagOrNull)
 {
   MOZ_ASSERT(aNode->IsContent());
   nsIFrame* relevantFrame =
     ServoRestyleManager::FrameForPseudoElement(aNode->AsContent(),
                                                aPseudoTagOrNull);
-  if (!relevantFrame) {
+  if (relevantFrame) {
+    return relevantFrame->StyleContext();
+  }
+
+  if (aPseudoTagOrNull) {
     return nullptr;
   }
 
-  return relevantFrame->StyleContext();
+  // FIXME(emilio): Is there a shorter path?
+  nsCSSFrameConstructor* fc =
+    aNode->OwnerDoc()->GetShell()->GetPresContext()->FrameConstructor();
+
+  // NB: This is only called for CalcStyleDifference, and we handle correctly
+  // the display: none case since Servo still has the older style.
+  return fc->GetDisplayContentsStyleFor(aNode->AsContent());
 }
 
 nsChangeHint
 Gecko_CalcStyleDifference(nsStyleContext* aOldStyleContext,
                           ServoComputedValuesBorrowed aComputedValues)
 {
   MOZ_ASSERT(aOldStyleContext);
   MOZ_ASSERT(aComputedValues);