diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -519,16 +519,19 @@ ParseContext<ParseHandler>::generateBind
 
     /*
      * Avoid pathological edge cases by explicitly limiting the total number of
      * bindings to what will fit in a uint32_t.
      */
     if (UINT32_MAX - args_.length() <= vars_.length() + bodyLevelLexicals_.length())
         return ts.reportError(JSMSG_TOO_MANY_LOCALS);
 
+    if (blockScopeDepth >= Bindings::BLOCK_SCOPED_LIMIT)
+        return ts.reportError(JSMSG_TOO_MANY_LOCALS);
+
     // Fix up slots in non-global contexts. In global contexts all body-level
     // names are dynamically defined and do not live in either frame or
     // CallObject slots.
     if (!sc->isGlobalContext()) {
         // Fix up the blockids of vars, whose static scope is always at the body
         // level. This could not be done up front in ParseContext::define, as
         // the original blockids are used for redeclaration checks.
         for (size_t i = 0; i < vars_.length(); i++)
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -247,16 +247,17 @@ class Bindings : public JS::Traceable
      * bindingArrayAndFlag_.
      */
     static const uintptr_t TEMPORARY_STORAGE_BIT = 0x1;
     bool bindingArrayUsingTemporaryStorage() const {
         return bindingArrayAndFlag_ & TEMPORARY_STORAGE_BIT;
     }
 
   public:
+    static const uint32_t BLOCK_SCOPED_LIMIT = UINT16_LIMIT;
 
     Binding* bindingArray() const {
         return reinterpret_cast<Binding*>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT);
     }
 
     Bindings()
       : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT),
         numArgs_(0), numBlockScoped_(0),
