Bug 1342439 part 1 - Replace macros to check for overrecursion with functions. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Sat, 25 Feb 2017 16:07:56 +0100
changeset 373989 4fbd0ba0c3d7a3c1a97af834157d52d1be1f188f
parent 373962 2dd1002e793bbce35bbd3e39e906028e4acd0107
child 373990 db093c5d86e6a93bd8f75667f39adb47ec1b1fe0
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1342439
milestone54.0a1
Bug 1342439 part 1 - Replace macros to check for overrecursion with functions. r=luke
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.cpp
js/src/builtin/Object.cpp
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/Parser.cpp
js/src/irregexp/RegExpEngine.cpp
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/Ion.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/arm/Simulator-arm.h
js/src/jit/arm64/vixl/Simulator-vixl.h
js/src/jit/mips32/Simulator-mips32.h
js/src/jit/mips64/Simulator-mips64.h
js/src/jsarray.cpp
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/jsiter.cpp
js/src/json.cpp
js/src/jsstr.cpp
js/src/proxy/Proxy.cpp
js/src/vm/EnvironmentObject.cpp
js/src/vm/Interpreter.cpp
js/src/vm/NativeObject.cpp
js/src/wasm/AsmJS.cpp
js/xpconnect/src/XPCVariant.cpp
--- 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
@@ -2986,17 +2986,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:
@@ -10103,17 +10104,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/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
@@ -1662,17 +1662,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/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/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/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)
--- 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/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
@@ -988,66 +988,81 @@ GetNativeStackLimit(JSContext* cx, int e
  * 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).
  */
 
-#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
+CheckRecursionLimit(JSContext* cx)
+{
+    return CheckRecursionLimit(cx, GetNativeStackLimit(cx));
+}
+
+MOZ_ALWAYS_INLINE bool
+CheckRecursionLimitDontReport(JSContext* cx, uintptr_t limit)
+{
+    int stackDummy;
+    return JS_CHECK_STACK_SIZE(limit, &stackDummy);
+}
+
+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/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/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2746,17 +2746,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
@@ -2987,17 +2988,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);
 }
--- 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/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()) {