Backed out changesets c5c333fa9772 and f12f1db4b4f1 (bug 1319416) for crashes and asserts on a CLOSED TREE.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Jan 2017 13:21:13 -0500
changeset 374160 963adce2ffbe5d8b0cd9acf8bb7fd6e85bde27d4
parent 374159 b0ce1a6d05ed1dd1979779c0cbcf942403f77312
child 374161 81159dae56440e1f412656b7f927d4c503d05384
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1319416
milestone53.0a1
backs outc5c333fa977212477cdb8c0ac0edab3ce617c073
f12f1db4b4f1893076c90d184a99d36a91c69485
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changesets c5c333fa9772 and f12f1db4b4f1 (bug 1319416) for crashes and asserts on a CLOSED TREE.
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/TokenStream.h
js/src/tests/ecma_6/ArrowFunctions/arrow-returning-arrow-with-block-body-followed-by-regexp.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2591,22 +2591,27 @@ Parser<ParseHandler>::newFunction(Handle
 #ifdef DEBUG
         if (isGlobalSelfHostedBuiltin)
             fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(false));
 #endif
     }
     return fun;
 }
 
+/*
+ * WARNING: Do not call this function directly.
+ * Call either matchOrInsertSemicolonAfterExpression or
+ * matchOrInsertSemicolonAfterNonExpression instead, depending on context.
+ */
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::matchOrInsertSemicolon()
+Parser<ParseHandler>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
 {
     TokenKind tt = TOK_EOF;
-    if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+    if (!tokenStream.peekTokenSameLine(&tt, modifier))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /*
          * When current token is `await` and it's outside of async function,
          * it's possibly intended to be an await expression.
          *
          *   await f();
          *        ^
@@ -2620,23 +2625,40 @@ Parser<ParseHandler>::matchOrInsertSemic
             tokenStream.currentToken().type == TOK_NAME &&
             tokenStream.currentName() == context->names().await)
         {
             error(JSMSG_AWAIT_OUTSIDE_ASYNC);
             return false;
         }
 
         /* Advance the scanner for proper error location reporting. */
-        tokenStream.consumeKnownToken(tt, TokenStream::Operand);
+        tokenStream.consumeKnownToken(tt, modifier);
         error(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
-
     bool matched;
-    return tokenStream.matchToken(&matched, TOK_SEMI, TokenStream::Operand);
+    if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
+        return false;
+    if (!matched && modifier == TokenStream::None)
+        tokenStream.addModifierException(TokenStream::OperandIsNone);
+    return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonAfterExpression()
+{
+    return matchOrInsertSemicolonHelper(TokenStream::None);
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonAfterNonExpression()
+{
+    return matchOrInsertSemicolonHelper(TokenStream::Operand);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::leaveInnerFunction(ParseContext* outerpc)
 {
     MOZ_ASSERT(pc != outerpc);
 
@@ -2989,17 +3011,17 @@ Parser<FullParseHandler>::skipLazyInnerF
     if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
         return false;
 
 #if JS_HAS_EXPR_CLOSURES
     // Only expression closure can be Statement kind.
     // If we remove expression closure, we can remove isExprBody flag from
     // LazyScript and JSScript.
     if (kind == Statement && funbox->isExprBody()) {
-        if (!matchOrInsertSemicolon())
+        if (!matchOrInsertSemicolonAfterExpression())
             return false;
     }
 #endif
 
     return true;
 }
 
 template <>
@@ -3414,18 +3436,17 @@ Parser<ParseHandler>::functionFormalPara
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_LC) {
         if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
             kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
-            IsConstructorKind(kind))
-        {
+            IsConstructorKind(kind)) {
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
         if (kind != Arrow) {
 #if JS_HAS_EXPR_CLOSURES
             addTelemetry(JSCompartment::DeprecatedExpressionClosure);
             if (!warnOnceAboutExprClosure())
@@ -3468,17 +3489,17 @@ Parser<ParseHandler>::functionFormalPara
         funbox->bufEnd = pos().end;
     } else {
 #if !JS_HAS_EXPR_CLOSURES
         MOZ_ASSERT(kind == Arrow);
 #endif
         if (tokenStream.hadError())
             return false;
         funbox->bufEnd = pos().end;
-        if (kind == Statement && !matchOrInsertSemicolon())
+        if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
             return false;
     }
 
     if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
         funbox->setNeedsHomeObject();
 
     if (!finishFunction(isStandaloneFunction))
         return false;
@@ -4306,25 +4327,34 @@ Parser<ParseHandler>::declarationPattern
             *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling);
             if (!*forInOrOfExpression)
                 return null();
 
             return pattern;
         }
     }
 
-    MUST_MATCH_TOKEN_MOD(TOK_ASSIGN, TokenStream::Operand, JSMSG_BAD_DESTRUCT_DECL);
+    MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
 
     Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
                            yieldHandling, TripledotProhibited);
     if (!init)
         return null();
 
     handler.checkAndSetIsDirectRHSAnonFunction(init);
 
+    if (forHeadKind) {
+        // For for(;;) declarations, consistency with |for (;| parsing requires
+        // that the ';' first be examined as Operand, even though absence of a
+        // binary operator (examined with modifier None) terminated |init|.
+        // For all other declarations, through ASI's infinite majesty, a next
+        // token on a new line would begin an expression.
+        tokenStream.addModifierException(TokenStream::OperandIsNone);
+    }
+
     return handler.newBinary(PNK_ASSIGN, pattern, init);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::initializerInNameDeclaration(Node decl, Node binding,
                                                    Handle<PropertyName*> name,
                                                    DeclarationKind declKind,
@@ -4341,49 +4371,74 @@ Parser<ParseHandler>::initializerInNameD
 
     Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed,
                                   yieldHandling, TripledotProhibited);
     if (!initializer)
         return false;
 
     handler.checkAndSetIsDirectRHSAnonFunction(initializer);
 
-    if (forHeadKind && initialDeclaration) {
-        bool isForIn, isForOf;
-        if (!matchInOrOf(&isForIn, &isForOf))
-            return false;
-
-        // An initialized declaration can't appear in a for-of:
-        //
-        //   for (var/let/const x = ... of ...); // BAD
-        if (isForOf) {
-            errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
-            return false;
-        }
-
-        if (isForIn) {
-            // Lexical declarations in for-in loops can't be initialized:
+    if (forHeadKind) {
+        if (initialDeclaration) {
+            bool isForIn, isForOf;
+            if (!matchInOrOf(&isForIn, &isForOf))
+                return false;
+
+            // An initialized declaration can't appear in a for-of:
             //
-            //   for (let/const x = ... in ...); // BAD
-            if (DeclarationKindIsLexical(declKind)) {
-                errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
+            //   for (var/let/const x = ... of ...); // BAD
+            if (isForOf) {
+                errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL);
                 return false;
             }
 
-            // This leaves only initialized for-in |var| declarations.  ES6
-            // forbids these; later ES un-forbids in non-strict mode code.
-            *forHeadKind = PNK_FORIN;
-            if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
+            if (isForIn) {
+                // Lexical declarations in for-in loops can't be initialized:
+                //
+                //   for (let/const x = ... in ...); // BAD
+                if (DeclarationKindIsLexical(declKind)) {
+                    errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL);
+                    return false;
+                }
+
+                // This leaves only initialized for-in |var| declarations.  ES6
+                // forbids these; later ES un-forbids in non-strict mode code.
+                *forHeadKind = PNK_FORIN;
+                if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT))
+                    return false;
+
+                *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling);
+                if (!*forInOrOfExpression)
+                    return false;
+            } else {
+                *forHeadKind = PNK_FORHEAD;
+            }
+        } else {
+            MOZ_ASSERT(*forHeadKind == PNK_FORHEAD);
+
+            // In the very rare case of Parser::assignExpr consuming an
+            // ArrowFunction with block body, when full-parsing with the arrow
+            // function being a skipped lazy inner function, we don't have
+            // lookahead for the next token.  Do a one-off peek here to be
+            // consistent with what Parser::matchForInOrOf does in the other
+            // arm of this |if|.
+            //
+            // If you think this all sounds pretty code-smelly, you're almost
+            // certainly correct.
+            TokenKind ignored;
+            if (!tokenStream.peekToken(&ignored))
                 return false;
-
-            *forInOrOfExpression = expressionAfterForInOrOf(PNK_FORIN, yieldHandling);
-            if (!*forInOrOfExpression)
-                return false;
-        } else {
-            *forHeadKind = PNK_FORHEAD;
+        }
+
+        if (*forHeadKind == PNK_FORHEAD) {
+            // Per Parser::forHeadStart, the semicolon in |for (;| is
+            // ultimately gotten as Operand.  But initializer expressions
+            // terminate with the absence of an operator gotten as None,
+            // so we need an exception.
+            tokenStream.addModifierException(TokenStream::OperandIsNone);
         }
     }
 
     return handler.finishInitializerAssignment(binding, initializer);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
@@ -4420,16 +4475,18 @@ Parser<ParseHandler>::declarationName(No
 
     if (matched) {
         if (!initializerInNameDeclaration(decl, binding, name, declKind, initialDeclaration,
                                           yieldHandling, forHeadKind, forInOrOfExpression))
         {
             return null();
         }
     } else {
+        tokenStream.addModifierException(TokenStream::NoneIsOperand);
+
         if (initialDeclaration && forHeadKind) {
             bool isForIn, isForOf;
             if (!matchInOrOf(&isForIn, &isForOf))
                 return null();
 
             if (isForIn) {
                 *forHeadKind = PNK_FORIN;
             } else if (isForOf) {
@@ -4517,17 +4574,17 @@ Parser<ParseHandler>::declarationList(Yi
 
         handler.addList(decl, binding);
 
         if (forHeadKind && *forHeadKind != PNK_FORHEAD)
             break;
 
         initialDeclaration = false;
 
-        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
     } while (matched);
 
     return decl;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
@@ -4540,17 +4597,17 @@ Parser<ParseHandler>::lexicalDeclaration
      * the same environment record as vars.
      *
      * However, they cannot be parsed exactly as vars, as ES6
      * requires that uninitialized lets throw ReferenceError on use.
      *
      * See 8.1.1.1.6 and the note in 13.2.1.
      */
     Node decl = declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET);
-    if (!decl || !matchOrInsertSemicolon())
+    if (!decl || !matchOrInsertSemicolonAfterExpression())
         return null();
 
     return decl;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
@@ -4781,17 +4838,17 @@ Parser<FullParseHandler>::importDeclarat
         error(JSMSG_DECLARATION_AFTER_IMPORT);
         return null();
     }
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     ParseNode* node =
         handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processImport(node))
         return null();
 
     return node;
@@ -4924,45 +4981,45 @@ Parser<FullParseHandler>::exportDeclarat
         //   from "foo"; // a single ExportDeclaration
         //
         // But if it doesn't, we might have an ASI opportunity in Operand
         // context, so simply matching a contextual keyword won't work:
         //
         //   export { x }   // ExportDeclaration, terminated by ASI
         //   fro\u006D      // ExpressionStatement, the name "from"
         //
-        // In that case let matchOrInsertSemicolon sort out ASI or any
-        // necessary error.
+        // In that case let matchOrInsertSemicolonAfterNonExpression sort out
+        // ASI or any necessary error.
         TokenKind tt;
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
 
         if (tt == TOK_NAME &&
             tokenStream.currentToken().name() == context->names().from &&
             !tokenStream.currentToken().nameContainsEscape())
         {
             MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
             Node moduleSpec = stringLiteral();
             if (!moduleSpec)
                 return null();
 
-            if (!matchOrInsertSemicolon())
+            if (!matchOrInsertSemicolonAfterNonExpression())
                 return null();
 
             ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
             if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
                 return null();
 
             return node;
         }
 
         tokenStream.ungetToken();
 
-        if (!matchOrInsertSemicolon())
+        if (!matchOrInsertSemicolonAfterNonExpression())
             return null();
         break;
       }
 
       case TOK_MUL: {
         kid = handler.newList(PNK_EXPORT_SPEC_LIST);
         if (!kid)
             return null();
@@ -4986,17 +5043,17 @@ Parser<FullParseHandler>::exportDeclarat
             return null();
 
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
         Node moduleSpec = stringLiteral();
         if (!moduleSpec)
             return null();
 
-        if (!matchOrInsertSemicolon())
+        if (!matchOrInsertSemicolonAfterNonExpression())
             return null();
 
         ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
         if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
             return null();
 
         return node;
 
@@ -5022,17 +5079,17 @@ Parser<FullParseHandler>::exportDeclarat
             return null();
         break;
       }
 
       case TOK_VAR:
         kid = declarationList(YieldIsName, PNK_VAR);
         if (!kid)
             return null();
-        if (!matchOrInsertSemicolon())
+        if (!matchOrInsertSemicolonAfterExpression())
             return null();
         if (!checkExportedNamesForDeclaration(kid))
             return null();
         break;
 
       case TOK_DEFAULT: {
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
@@ -5075,17 +5132,17 @@ Parser<FullParseHandler>::exportDeclarat
             nameNode = newName(name);
             if (!nameNode)
                 return null();
             if (!noteDeclaredName(name, DeclarationKind::Const, pos()))
                 return null();
             kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
             if (!kid)
                 return null();
-            if (!matchOrInsertSemicolon())
+            if (!matchOrInsertSemicolonAfterExpression())
                 return null();
             break;
           }
         }
 
         ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode,
                                                               TokenPos(begin, pos().end));
         if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
@@ -5140,17 +5197,17 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
     Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited,
                        /* possibleError = */ nullptr, invoked);
     if (!pnexpr)
         return null();
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterExpression())
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <class ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
 {
@@ -5331,17 +5388,17 @@ Parser<ParseHandler>::whileStatement(Yie
     return handler.newWhileStatement(begin, cond, body);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
 {
     TokenKind tt;
-    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+    if (!tokenStream.getToken(&tt))
         return false;
 
     *isForInp = tt == TOK_IN;
     *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of;
     if (!*isForInp && !*isForOfp) {
         tokenStream.ungetToken();
     } else {
         if (tt == TOK_NAME && !checkUnescapedName())
@@ -5444,22 +5501,24 @@ Parser<ParseHandler>::forHeadStart(Yield
     if (!*forInitialPart)
         return false;
 
     bool isForIn, isForOf;
     if (!matchInOrOf(&isForIn, &isForOf))
         return false;
 
     // If we don't encounter 'in'/'of', we have a for(;;) loop.  We've handled
-    // the init expression; the caller handles the rest.
+    // the init expression; the caller handles the rest.  Allow the Operand
+    // modifier when regetting: Operand must be used to examine the ';' in
+    // |for (;|, and our caller handles this case and that.
     if (!isForIn && !isForOf) {
         if (!possibleError.checkForExpressionError())
             return false;
-
         *forHeadKind = PNK_FORHEAD;
+        tokenStream.addModifierException(TokenStream::OperandIsNone);
         return true;
     }
 
     MOZ_ASSERT(isForIn != isForOf);
 
     // In a for-of loop, 'let' that starts the loop head is a |let| keyword,
     // per the [lookahead ≠ let] restriction on the LeftHandSideExpression
     // variant of such loops.  Expressions that start with |let| can't be used
@@ -5830,17 +5889,17 @@ Parser<ParseHandler>::continueStatement(
             if (foundTarget)
                 break;
         }
     } else if (!pc->findInnermostStatement(isLoop)) {
         error(JSMSG_BAD_CONTINUE);
         return null();
     }
 
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     return handler.newContinueStatement(label, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
@@ -5870,17 +5929,17 @@ Parser<ParseHandler>::breakStatement(Yie
         };
 
         if (!pc->findInnermostStatement(isBreakTarget)) {
             errorAt(begin, JSMSG_TOUGH_BREAK);
             return null();
         }
     }
 
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
 
     return handler.newBreakStatement(label, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
@@ -5910,20 +5969,20 @@ Parser<ParseHandler>::returnStatement(Yi
         exprNode = expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!exprNode)
             return null();
         pc->funHasReturnExpr = true;
       }
     }
 
     if (exprNode) {
-        if (!matchOrInsertSemicolon())
+        if (!matchOrInsertSemicolonAfterExpression())
             return null();
     } else {
-        if (!matchOrInsertSemicolon())
+        if (!matchOrInsertSemicolonAfterNonExpression())
             return null();
     }
 
     Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
     if (!pn)
         return null();
 
     /* Disallow "return v;" in legacy generators. */
@@ -6211,17 +6270,17 @@ Parser<ParseHandler>::throwStatement(Yie
         error(JSMSG_LINE_BREAK_AFTER_THROW);
         return null();
     }
 
     Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!throwExpr)
         return null();
 
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterExpression())
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
@@ -6438,17 +6497,17 @@ Parser<ParseHandler>::catchBlockStatemen
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::debuggerStatement()
 {
     TokenPos p;
     p.begin = pos().begin;
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
     p.end = pos().end;
 
     pc->sc()->setBindingsAccessedDynamically();
     pc->sc()->setHasDebuggerStatement();
 
     return handler.newDebuggerStatement(p);
 }
@@ -6745,17 +6804,17 @@ Parser<ParseHandler>::nextTokenContinues
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
         return null();
-    if (!matchOrInsertSemicolon())
+    if (!matchOrInsertSemicolonAfterExpression())
         return null();
     return vars;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statement(YieldHandling yieldHandling)
 {
@@ -7150,17 +7209,17 @@ Parser<ParseHandler>::expr(InHandling in
                            InvokedPrediction invoked /* = PredictUninvoked */)
 {
     Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
                          possibleError, invoked);
     if (!pn)
         return null();
 
     bool matched;
-    if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+    if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (!matched)
         return pn;
 
     Node seq = handler.newCommaExpressionList(pn);
     if (!seq)
         return null();
     while (true) {
@@ -7205,22 +7264,21 @@ Parser<ParseHandler>::expr(InHandling in
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
             possibleErrorInner.transferErrorsTo(possibleError);
         }
 
         handler.addList(seq, pn);
 
-        if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
+        if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
         if (!matched)
             break;
     }
-
     return seq;
 }
 
 static const JSOp ParseNodeKindToJSOp[] = {
     JSOP_OR,
     JSOP_AND,
     JSOP_BITOR,
     JSOP_BITXOR,
@@ -7300,39 +7358,39 @@ Precedence(ParseNodeKind pnk) {
 
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::orExpr(InHandling inHandling, YieldHandling yieldHandling,
-                             TripledotHandling tripledotHandling,
-                             PossibleError* possibleError,
-                             InvokedPrediction invoked /* = PredictUninvoked */)
+Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
+                              TripledotHandling tripledotHandling,
+                              PossibleError* possibleError,
+                              InvokedPrediction invoked /* = PredictUninvoked */)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
     Node pn;
     for (;;) {
         pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
         if (!pn)
-            return null();
+            return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
-        if (!tokenStream.getToken(&tok, TokenStream::None))
+        if (!tokenStream.getToken(&tok))
             return null();
 
         ParseNodeKind pnk;
         if (tok == TOK_IN ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) {
             // We're definitely not in a destructuring context, so report any
             // pending expression error now.
             if (possibleError && !possibleError->checkForExpressionError())
                 return null();
@@ -7358,67 +7416,58 @@ Parser<ParseHandler>::orExpr(InHandling 
         // job to decide if the operator in question is left- or
         // right-associative, and build the corresponding tree.
         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
             depth--;
             ParseNodeKind combiningPnk = kindStack[depth];
             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
             pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
             if (!pn)
-                return null();
+                return pn;
         }
 
         if (pnk == PNK_LIMIT)
             break;
 
         nodeStack[depth] = pn;
         kindStack[depth] = pnk;
         depth++;
         MOZ_ASSERT(depth <= PRECEDENCE_CLASSES);
     }
 
-    // When the next token is no longer a binary operator, it's potentially the
-    // start of an expression.  Add a modifier exception so that '/' and so on
-    // are now interpreted as the start of an expression.
-    tokenStream.ungetToken();
-    tokenStream.addModifierException(TokenStream::OperandIsNone);
-
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::condExpr(InHandling inHandling, YieldHandling yieldHandling,
-                               TripledotHandling tripledotHandling,
-                               PossibleError* possibleError,
-                               InvokedPrediction invoked /* = PredictUninvoked */)
-{
-    Node condition = orExpr(inHandling, yieldHandling, tripledotHandling, possibleError, invoked);
-    if (!condition)
-        return null();
-
-    TokenKind next;
-    if (!tokenStream.peekToken(&next, TokenStream::Operand))
-        return null();
-    if (next != TOK_HOOK)
+Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
+                                TripledotHandling tripledotHandling,
+                                PossibleError* possibleError,
+                                InvokedPrediction invoked /* = PredictUninvoked */)
+{
+    Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked);
+
+    if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
 
-    tokenStream.consumeKnownToken(TOK_HOOK, TokenStream::Operand);
-
     Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!thenExpr)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_COLON, TokenStream::Operand, JSMSG_COLON_IN_COND);
+    MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
     Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     if (!elseExpr)
         return null();
 
+    // Advance to the next token; the caller is responsible for interpreting it.
+    TokenKind ignored;
+    if (!tokenStream.getToken(&ignored))
+        return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
 class AutoClearInDestructuringDecl
 {
     ParseContext* pc_;
     Maybe<DeclarationKind> saved_;
 
@@ -7449,17 +7498,17 @@ Parser<ParseHandler>::assignExpr(InHandl
     // 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.)
     //
     // In such cases, we can avoid the full expression parsing route through
-    // assignExpr(), condExpr(), orExpr(), unaryExpr(), memberExpr(), and
+    // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
     // primaryExpr().
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     uint32_t exprOffset = pos().begin;
 
@@ -7516,70 +7565,77 @@ Parser<ParseHandler>::assignExpr(InHandl
 
     PossibleError possibleErrorInner(*this);
     Node lhs;
     if (maybeAsyncArrow) {
         tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
         MOZ_ASSERT(tokenStream.currentName() == context->names().async);
         MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
 
+        TokenKind tt;
         if (!tokenStream.getToken(&tt))
             return null();
         MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
 
         // Check yield validity here.
         RootedPropertyName name(context, bindingIdentifier(yieldHandling));
         if (!name)
             return null();
 
-        if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        if (!tokenStream.getToken(&tt))
             return null();
         if (tt != TOK_ARROW) {
             error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(tt));
 
             return null();
         }
     } else {
-        lhs = condExpr(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
-        if (!lhs)
-            return null();
-
-        if (!tokenStream.getToken(&tt, TokenStream::Operand))
-            return null();
+        lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
+        if (!lhs) {
+            return null();
+        }
     }
 
     ParseNodeKind kind;
     JSOp op;
-    switch (tt) {
+    switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
       case TOK_SUBASSIGN:    kind = PNK_SUBASSIGN;    op = JSOP_SUB;    break;
       case TOK_BITORASSIGN:  kind = PNK_BITORASSIGN;  op = JSOP_BITOR;  break;
       case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
       case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
       case TOK_LSHASSIGN:    kind = PNK_LSHASSIGN;    op = JSOP_LSH;    break;
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    op = JSOP_RSH;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   op = JSOP_URSH;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    op = JSOP_MUL;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
       case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    op = JSOP_POW;    break;
 
       case TOK_ARROW: {
+
         // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
         tokenStream.ungetToken();
         TokenKind next;
-        if (!tokenStream.peekTokenSameLine(&next, TokenStream::Operand))
+        if (!tokenStream.peekTokenSameLine(&next))
             return null();
         MOZ_ASSERT(next == TOK_ARROW || next == TOK_EOL);
 
         if (next != TOK_ARROW) {
             error(JSMSG_LINE_BREAK_BEFORE_ARROW);
             return null();
         }
+        tokenStream.consumeKnownToken(TOK_ARROW);
+
+        bool isBlock = false;
+        if (!tokenStream.peekToken(&next, TokenStream::Operand))
+            return null();
+        if (next == TOK_LC)
+            isBlock = true;
 
         tokenStream.seek(start);
 
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
         GeneratorKind generatorKind = NotGenerator;
         FunctionAsyncKind asyncKind = SyncFunction;
@@ -7604,18 +7660,52 @@ Parser<ParseHandler>::assignExpr(InHandl
                 tokenStream.ungetToken();
             }
         }
 
         Node pn = handler.newArrowFunction();
         if (!pn)
             return null();
 
-        return functionDefinition(pn, inHandling, yieldHandling, nullptr, Arrow, generatorKind,
-                                  asyncKind);
+        Node arrowFunc = functionDefinition(pn, inHandling, yieldHandling, nullptr,
+                                            Arrow, generatorKind, asyncKind);
+        if (!arrowFunc)
+            return null();
+
+        if (isBlock) {
+            // This arrow function could be a non-trailing member of a comma
+            // expression or a semicolon terminating a full expression.  If so,
+            // the next token is that comma/semicolon, gotten with None:
+            //
+            //   a => {}, b; // as if (a => {}), b;
+            //   a => {};
+            //
+            // But if this arrow function ends a statement, ASI permits the
+            // next token to start an expression statement.  In that case the
+            // next token must be gotten as Operand:
+            //
+            //   a => {} // complete expression statement
+            //   /x/g;   // regular expression as a statement, *not* division
+            //
+            // Getting the second case right requires the first token-peek
+            // after the arrow function use Operand, and that peek must occur
+            // before Parser::expr() looks for a comma.  Do so here, then
+            // immediately add the modifier exception needed for the first
+            // case.
+            //
+            // Note that the second case occurs *only* if the arrow function
+            // has block body.  An arrow function not ending in such, ends in
+            // another AssignmentExpression that we can inductively assume was
+            // peeked consistently.
+            TokenKind ignored;
+            if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
+                return null();
+            tokenStream.addModifierException(TokenStream::NoneIsOperand);
+        }
+        return arrowFunc;
       }
 
       default:
         MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
         if (!possibleError) {
             if (!possibleErrorInner.checkForExpressionError())
                 return null();
         } else {
@@ -7924,17 +8014,17 @@ Parser<ParseHandler>::generatorComprehen
     Node body = handler.newStatementList(TokenPos(begin, pos().end));
     if (!body)
         return null();
 
     Node comp = comprehension(StarGenerator);
     if (!comp)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
+    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
 
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, pos().end);
     handler.addStatementToList(body, comp);
     handler.setEndPosition(body, pos().end);
     handler.setBeginPosition(genfn, begin);
     handler.setEndPosition(genfn, pos().end);
 
@@ -7989,17 +8079,17 @@ Parser<ParseHandler>::comprehensionFor(G
         error(JSMSG_OF_AFTER_FOR_NAME);
         return null();
     }
 
     Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!rhs)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
+    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
 
     TokenPos headPos(begin, pos().end);
 
     ParseContext::Scope scope(this);
     if (!scope.init(pc))
         return null();
 
     {
@@ -8038,17 +8128,17 @@ Parser<ParseHandler>::comprehensionIf(Ge
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!cond)
         return null();
-    MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
+    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(cond)) {
         if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
             return null();
     }
 
     Node then = comprehensionTail(comprehensionKind);
@@ -8118,17 +8208,17 @@ Parser<ParseHandler>::comprehension(Gene
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::arrayComprehension(uint32_t begin)
 {
     Node inner = comprehension(NotGenerator);
     if (!inner)
         return null();
 
-    MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
+    MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
 
     Node comp = handler.newList(PNK_ARRAYCOMP, inner);
     if (!comp)
         return null();
 
     handler.setBeginPosition(comp, begin);
     handler.setEndPosition(comp, pos().end);
 
@@ -8674,36 +8764,35 @@ Parser<ParseHandler>::arrayInitializer(Y
         /*
          * Mark empty arrays as non-constant, since we cannot easily
          * determine their type.
          */
         handler.setListFlag(literal, PNX_NONCONST);
     } else {
         tokenStream.ungetToken();
 
-        for (uint32_t index = 0; ; index++) {
+        uint32_t index = 0;
+        TokenStream::Modifier modifier = TokenStream::Operand;
+        for (; ; index++) {
             if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
                 error(JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
             TokenKind tt;
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
             if (tt == TOK_RB)
                 break;
 
             if (tt == TOK_COMMA) {
                 tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
                 if (!handler.addElision(literal, pos()))
                     return null();
-                continue;
-            }
-
-            if (tt == TOK_TRIPLEDOT) {
+            } else if (tt == TOK_TRIPLEDOT) {
                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
                 Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
                                         possibleError);
                 if (!inner)
                     return null();
                 if (!handler.addSpreadElement(literal, begin, inner))
                     return null();
@@ -8712,29 +8801,32 @@ Parser<ParseHandler>::arrayInitializer(Y
                                           possibleError);
                 if (!element)
                     return null();
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
-            bool matched;
-            if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
-                return null();
-            if (!matched)
-                break;
-
-            if (tt == TOK_TRIPLEDOT && possibleError)
-                possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
-        }
-
-        MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_AFTER_LIST);
-    }
-
+            if (tt != TOK_COMMA) {
+                /* If we didn't already match TOK_COMMA in above case. */
+                bool matched;
+                if (!tokenStream.matchToken(&matched, TOK_COMMA))
+                    return null();
+                if (!matched) {
+                    modifier = TokenStream::None;
+                    break;
+                }
+                if (tt == TOK_TRIPLEDOT && possibleError)
+                    possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA);
+            }
+        }
+
+        MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST);
+    }
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 static JSAtom*
 DoubleToAtom(ExclusiveContext* cx, double value)
 {
     // This is safe because doubles can not be moved.
@@ -9288,17 +9380,17 @@ Parser<ParseHandler>::primaryExpr(YieldH
       case TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
         if (next == TOK_RP) {
             // Not valid expression syntax, but this is valid in an arrow function
             // with no params: `() => body`.
-            tokenStream.consumeKnownToken(TOK_RP, TokenStream::Operand);
+            tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
             if (!tokenStream.peekToken(&next))
                 return null();
             if (next != TOK_ARROW) {
                 error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TOK_RP));
                 return null();
             }
 
@@ -9313,17 +9405,17 @@ Parser<ParseHandler>::primaryExpr(YieldH
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
             return generatorComprehension(begin);
         }
 
         // Pass |possibleError| to support destructuring in arrow parameters.
         Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
         if (!expr)
             return null();
-        MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
+        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
         handler.setEndPosition(expr, pos().end);
         return handler.parenthesize(expr);
       }
 
       case TOK_TEMPLATE_HEAD:
         return templateLiteral(yieldHandling);
 
       case TOK_NO_SUBS_TEMPLATE:
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1239,24 +1239,24 @@ class Parser final : public ParserBase, 
     Node expr(InHandling inHandling, YieldHandling yieldHandling,
               TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
               InvokedPrediction invoked = PredictUninvoked);
     Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                     TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr,
                     InvokedPrediction invoked = PredictUninvoked);
     Node assignExprWithoutYieldOrAwait(YieldHandling yieldHandling);
     Node yieldExpression(InHandling inHandling);
-    Node condExpr(InHandling inHandling, YieldHandling yieldHandling,
-                  TripledotHandling tripledotHandling,
-                  PossibleError* possibleError,
-                  InvokedPrediction invoked = PredictUninvoked);
-    Node orExpr(InHandling inHandling, YieldHandling yieldHandling,
-                TripledotHandling tripledotHandling,
-                PossibleError* possibleError,
-                InvokedPrediction invoked = PredictUninvoked);
+    Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
+                   TripledotHandling tripledotHandling,
+                   PossibleError* possibleError,
+                   InvokedPrediction invoked = PredictUninvoked);
+    Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
+                 TripledotHandling tripledotHandling,
+                 PossibleError* possibleError,
+                 InvokedPrediction invoked = PredictUninvoked);
     Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                    PossibleError* possibleError = nullptr,
                    InvokedPrediction invoked = PredictUninvoked);
     Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                     TokenKind tt, bool allowCallSyntax = true,
                     PossibleError* possibleError = nullptr,
                     InvokedPrediction invoked = PredictUninvoked);
     Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
@@ -1339,18 +1339,16 @@ class Parser final : public ParserBase, 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
         return options().forEachStatementOption && versionNumber() >= JSVERSION_1_6;
 #endif
     }
 
-    // Indicate if the next token (tokenized as Operand) is |in| or |of|.  If
-    // so, consume it.
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool hasUsedFunctionSpecialName(HandlePropertyName name);
     bool declareFunctionArgumentsObject();
     bool declareFunctionThis();
     Node newInternalDotName(HandlePropertyName name);
     Node newThisName();
     Node newDotGeneratorName();
@@ -1366,17 +1364,19 @@ class Parser final : public ParserBase, 
                                      YieldHandling yieldHandling, FunctionSyntaxKind kind,
                                      GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
                                      bool tryAnnexB,
                                      Directives inheritedDirectives, Directives* newDirectives);
     bool finishFunctionScopes(bool isStandaloneFunction);
     bool finishFunction(bool isStandaloneFunction = false);
     bool leaveInnerFunction(ParseContext* outerpc);
 
-    bool matchOrInsertSemicolon();
+    bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier);
+    bool matchOrInsertSemicolonAfterExpression();
+    bool matchOrInsertSemicolonAfterNonExpression();
 
   public:
     enum FunctionCallBehavior {
         PermitAssignmentToFunctionCalls,
         ForbidAssignmentToFunctionCalls
     };
 
     bool isValidSimpleAssignmentTarget(Node node,
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -453,26 +453,18 @@ class MOZ_STACK_CLASS TokenStream
     static constexpr ModifierException NoException = Token::NoException;
     static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand;
     static constexpr ModifierException OperandIsNone = Token::OperandIsNone;
     static constexpr ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName;
 
     void addModifierException(ModifierException modifierException) {
 #ifdef DEBUG
         const Token& next = nextToken();
-
-        // Permit adding the same exception multiple times.  This is important
-        // particularly for Parser::assignExpr's early fast-path cases and
-        // arrow function parsing: we want to add modifier exceptions in the
-        // fast paths, then potentially (but not necessarily) duplicate them
-        // after parsing all of an arrow function.
-        if (next.modifierException == modifierException)
-            return;
-
-        if (next.modifierException == NoneIsOperand) {
+        if (next.modifierException == NoneIsOperand)
+        {
             // Token after yield expression without operand already has
             // NoneIsOperand exception.
             MOZ_ASSERT(modifierException == OperandIsNone);
             MOZ_ASSERT(next.type != TOK_DIV,
                        "next token requires contextual specifier to be parsed unambiguously");
 
             // Do not update modifierException.
             return;
@@ -690,23 +682,16 @@ class MOZ_STACK_CLASS TokenStream
         return true;
     }
 
     MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) {
         TokenKind tt;
         if (!peekToken(&tt))
             return false;
         *endsExpr = isExprEnding[tt];
-        if (*endsExpr) {
-            // If the next token ends an overall Expression, we'll parse this
-            // Expression without ever invoking Parser::orExpr().  But we need
-            // that function's side effect of adding this modifier exception,
-            // so we have to do it manually here.
-            addModifierException(OperandIsNone);
-        }
         return true;
     }
 
     class MOZ_STACK_CLASS Position {
       public:
         // The Token fields may contain pointers to atoms, so for correct
         // rooting we must ensure collection of atoms is disabled while objects
         // of this class are live.  Do this by requiring a dummy AutoKeepAtoms
deleted file mode 100644
--- a/js/src/tests/ecma_6/ArrowFunctions/arrow-returning-arrow-with-block-body-followed-by-regexp.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* 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/. */
-
-function t()
-{
-  var x = y => z => {} // ASI occurs here
-  /Q/;
-  return 42;
-}
-
-assertEq(t(), 42);
-
-if (typeof reportCompare === "function")
-  reportCompare(true, true);