Bug 1066827 - Part2: Check error in TokenStream.getToken. r=jwalden
☠☠ backed out by b3f9fa3a60f3 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Fri, 24 Oct 2014 20:59:00 +0200
changeset 212607 bc56d0664d0282c691a3273323c89ef56ab5d43e
parent 212606 33ee15f0fbcc0b3ab98253e621acdbdfc16f0553
child 212608 09e37ba34968d6b850c9057152ae1f50a95f6461
push id27721
push usercbook@mozilla.com
push dateTue, 28 Oct 2014 14:55:05 +0000
treeherdermozilla-central@c0ddb1b098ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1066827
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1066827 - Part2: Check error in TokenStream.getToken. r=jwalden
js/src/asmjs/AsmJSValidate.cpp
js/src/frontend/Parser.cpp
js/src/frontend/TokenStream.h
js/src/jsfun.cpp
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -6859,25 +6859,26 @@ CheckChangeHeap(ModuleCompiler &m, Parse
     return m.addChangeHeap(changeHeapName, fn, mask, min, max);
 }
 
 static bool
 ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
 {
     TokenStream &tokenStream = m.tokenStream();
 
-    DebugOnly<TokenKind> tk = tokenStream.getToken();
-    MOZ_ASSERT(tk == TOK_FUNCTION);
+    tokenStream.consumeKnownToken(TOK_FUNCTION);
 
     RootedPropertyName name(m.cx());
 
-    TokenKind tt = tokenStream.getToken();
-    if (tt == TOK_NAME) {
+    TokenKind tk;
+    if (!tokenStream.getToken(&tk))
+        return false;
+    if (tk == TOK_NAME) {
         name = tokenStream.currentName();
-    } else if (tt == TOK_YIELD) {
+    } else if (tk == TOK_YIELD) {
         if (!m.parser().checkYieldNameValidity())
             return false;
         name = m.cx()->names().yield;
     } else {
         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
     }
 
     ParseNode *fn = m.parser().handler.newFunctionDefinition();
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -54,20 +54,21 @@ typedef Rooted<StaticBlockObject*> Roote
 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
 typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
 
 
 /* Read a token. Report an error and return null() if that token isn't of type tt. */
 #define MUST_MATCH_TOKEN(tt, errno)                                                         \
     JS_BEGIN_MACRO                                                                          \
-        TokenKind token = tokenStream.getToken();                                           \
+        TokenKind token;                                                                    \
+        if (!tokenStream.getToken(&token))                                                  \
+            return null();                                                                  \
         if (token != tt) {                                                                  \
-            if (token != TOK_ERROR)                                                         \
-                report(ParseError, false, null(), errno);                                   \
+            report(ParseError, false, null(), errno);                                       \
             return null();                                                                  \
         }                                                                                   \
     JS_END_MACRO
 
 static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
 
 template <typename ParseHandler>
 bool
@@ -1221,17 +1222,17 @@ Parser<ParseHandler>::newFunction(Generi
 static bool
 MatchOrInsertSemicolon(TokenStream &ts)
 {
     TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
     if (tt == TOK_ERROR)
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /* Advance the scanner for proper error location reporting. */
-        ts.getToken(TokenStream::Operand);
+        ts.consumeKnownToken(tt);
         ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
     (void) ts.matchToken(TOK_SEMI);
     return true;
 }
 
 template <typename ParseHandler>
@@ -1532,17 +1533,20 @@ Parser<ParseHandler>::functionArguments(
     if (kind == Arrow) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt))
             return false;
         if (tt == TOK_NAME)
             parenFreeArrow = true;
     }
     if (!parenFreeArrow) {
-        if (tokenStream.getToken() != TOK_LP) {
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return false;
+        if (tt != TOK_LP) {
             report(ParseError, false, null(),
                    kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
             return false;
         }
 
         // Record the start of function source (for FunctionToString). If we
         // are parenFreeArrow, we will set this below, after consuming the NAME.
         funbox->setStart(tokenStream);
@@ -1559,17 +1563,19 @@ Parser<ParseHandler>::functionArguments(
         Node list = null();
 
         do {
             if (*hasRest) {
                 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
                 return false;
             }
 
-            TokenKind tt = tokenStream.getToken();
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
+                return false;
             MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
             switch (tt) {
               case TOK_LB:
               case TOK_LC:
               {
                 /* See comment below in the TOK_NAME case. */
                 if (duplicatedArg) {
                     report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
@@ -1627,20 +1633,20 @@ Parser<ParseHandler>::functionArguments(
               case TOK_YIELD:
                 if (!checkYieldNameValidity())
                     return false;
                 goto TOK_NAME;
 
               case TOK_TRIPLEDOT:
               {
                 *hasRest = true;
-                tt = tokenStream.getToken();
+                if (!tokenStream.getToken(&tt))
+                    return false;
                 if (tt != TOK_NAME) {
-                    if (tt != TOK_ERROR)
-                        report(ParseError, false, null(), JSMSG_NO_REST_NAME);
+                    report(ParseError, false, null(), JSMSG_NO_REST_NAME);
                     return false;
                 }
                 goto TOK_NAME;
               }
 
               TOK_NAME:
               case TOK_NAME:
               {
@@ -1680,25 +1686,28 @@ Parser<ParseHandler>::functionArguments(
                     handler.setLastFunctionArgumentDefault(funcpn, def_expr);
                 }
 
                 break;
               }
 
               default:
                 report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
-                /* FALL THROUGH */
-              case TOK_ERROR:
                 return false;
             }
         } while (!parenFreeArrow && tokenStream.matchToken(TOK_COMMA));
 
-        if (!parenFreeArrow && tokenStream.getToken() != TOK_RP) {
-            report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
-            return false;
+        if (!parenFreeArrow) {
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
+                return false;
+            if (tt != TOK_RP) {
+                report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
+                return false;
+            }
         }
 
         if (!hasDefaults)
             funbox->length = pc->numArgs() - *hasRest;
     }
 
     return true;
 }
@@ -1990,27 +1999,24 @@ template <typename ParseHandler>
 bool
 Parser<ParseHandler>::addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt)
 {
     Node pn = expr();
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
-    tt = tokenStream.getToken();
-    if (tt != TOK_RC) {
-        if (tt != TOK_ERROR)
-            report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
+    if (!tokenStream.getToken(&tt))
         return false;
-    }
-
-    tt = tokenStream.getToken(TokenStream::TemplateTail);
-    if (tt == TOK_ERROR)
+    if (tt != TOK_RC) {
+        report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
         return false;
-    return true;
+    }
+
+    return tokenStream.getToken(&tt, TokenStream::TemplateTail);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::taggedTemplate(Node nodeList, TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin, pc->blockidGen);
     if (!callSiteObjNode)
@@ -2434,17 +2440,20 @@ Parser<ParseHandler>::functionArgsAndBod
 
     if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) {
         report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
         return false;
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
-    if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return false;
+    if (tt != TOK_LC) {
         if (funbox->isStarGenerator()) {
             report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
         if (kind != Arrow)
             sawDeprecatedExpressionClosure = true;
 
@@ -2497,31 +2506,32 @@ Parser<ParseHandler>::checkYieldNameVali
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionStmt()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     RootedPropertyName name(context);
     GeneratorKind generatorKind = NotGenerator;
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
 
     if (tt == TOK_MUL) {
         generatorKind = StarGenerator;
-        tt = tokenStream.getToken();
+        if (!tokenStream.getToken(&tt))
+            return null();
     }
 
     if (tt == TOK_NAME) {
         name = tokenStream.currentName();
     } else if (tt == TOK_YIELD) {
         if (!checkYieldNameValidity())
             return null();
         name = tokenStream.currentName();
-    } else if (tt == TOK_ERROR) {
-        return null();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
         return null();
     }
 
     /* We forbid function statements in strict mode code. */
     if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
@@ -2533,32 +2543,33 @@ Parser<ParseHandler>::functionStmt()
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionExpr()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     GeneratorKind generatorKind = NotGenerator;
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
 
     if (tt == TOK_MUL) {
         generatorKind = StarGenerator;
-        tt = tokenStream.getToken();
+        if (!tokenStream.getToken(&tt))
+            return null();
     }
 
     RootedPropertyName name(context);
     if (tt == TOK_NAME) {
         name = tokenStream.currentName();
     } else if (tt == TOK_YIELD) {
         if (!checkYieldNameValidity())
             return null();
         name = tokenStream.currentName();
-    } else if (tt == TOK_ERROR) {
-        return null();
     } else {
         tokenStream.ungetToken();
     }
 
     return functionDef(name, Normal, Expression, generatorKind);
 }
 
 /*
@@ -3660,17 +3671,19 @@ Parser<ParseHandler>::variables(ParseNod
 
     bool first = true;
     Node pn2;
     do {
         if (psimple && !first)
             *psimple = false;
         first = false;
 
-        TokenKind tt = tokenStream.getToken();
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return null();
         if (tt == TOK_LB || tt == TOK_LC) {
             if (psimple)
                 *psimple = false;
 
             pc->inDeclDestructuring = true;
             pn2 = primaryExpr(tt);
             pc->inDeclDestructuring = false;
             if (!pn2)
@@ -3707,18 +3720,17 @@ Parser<ParseHandler>::variables(ParseNod
             continue;
         }
 
         if (tt != TOK_NAME) {
             if (tt == TOK_YIELD) {
                 if (!checkYieldNameValidity())
                     return null();
             } else {
-                if (tt != TOK_ERROR)
-                    report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
+                report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
                 return null();
             }
         }
 
         RootedPropertyName name(context, tokenStream.currentName());
         pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
         if (!pn2)
             return null();
@@ -3937,17 +3949,19 @@ Parser<ParseHandler>::importDeclaration(
     MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
 
     if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
         report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
 
     Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
     if (!importSpecSet)
         return null();
 
     if (tt == TOK_NAME || tt == TOK_LC) {
         if (tt == TOK_NAME) {
             // Handle the form |import a from 'b'|, by adding a single import
@@ -3980,20 +3994,22 @@ Parser<ParseHandler>::importDeclaration(
                 // If the next token is a keyword, the previous call to
                 // peekToken matched it as a TOK_NAME, and put it in the
                 // lookahead buffer, so this call will match keywords as well.
                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
                 Node importName = newName(tokenStream.currentName());
                 if (!importName)
                     return null();
 
-                if (tokenStream.getToken() == TOK_NAME &&
-                    tokenStream.currentName() == context->names().as)
-                {
-                    if (tokenStream.getToken() != TOK_NAME) {
+                if (!tokenStream.getToken(&tt))
+                    return null();
+                if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
+                    if (!tokenStream.getToken(&tt))
+                        return null();
+                    if (tt != TOK_NAME) {
                         report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
                         return null();
                     }
                 } else {
                     // Keywords cannot be bound to themselves, so an import name
                     // that is a keyword is a syntax error if it is not followed
                     // by the keyword 'as'.
                     if (IsKeyword(importName->name())) {
@@ -4014,19 +4030,19 @@ Parser<ParseHandler>::importDeclaration(
                     return null();
 
                 handler.addList(importSpecSet, importSpec);
             } while (tokenStream.matchToken(TOK_COMMA));
 
             MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
         }
 
-        if (tokenStream.getToken() != TOK_NAME ||
-            tokenStream.currentName() != context->names().from)
-        {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
             report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
             return null();
         }
 
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
     } else {
         if (tt != TOK_STRING) {
             report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
@@ -4066,17 +4082,20 @@ Parser<ParseHandler>::exportDeclaration(
     if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
         report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
 
     Node kid;
-    switch (TokenKind tt = tokenStream.getToken()) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
+    switch (tt) {
       case TOK_LC:
       case TOK_MUL:
         kid = handler.newList(PNK_EXPORT_SPEC_LIST);
         if (!kid)
             return null();
 
         if (tt == TOK_LC) {
             do {
@@ -4088,20 +4107,22 @@ Parser<ParseHandler>::exportDeclaration(
                 if (tt == TOK_RC)
                     break;
 
                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
                 Node bindingName = newName(tokenStream.currentName());
                 if (!bindingName)
                     return null();
 
-                if (tokenStream.getToken() == TOK_NAME &&
-                    tokenStream.currentName() == context->names().as)
-                {
-                    if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME) {
+                if (!tokenStream.getToken(&tt))
+                    return null();
+                if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
+                    if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+                        return null();
+                    if (tt != TOK_NAME) {
                         report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
                         return null();
                     }
                 } else {
                     tokenStream.ungetToken();
                 }
                 Node exportName = newName(tokenStream.currentName());
                 if (!exportName)
@@ -4119,19 +4140,19 @@ Parser<ParseHandler>::exportDeclaration(
             // Handle the form |export *| by adding a special export batch
             // specifier to the list.
             Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
             if (!kid)
                 return null();
 
             handler.addList(kid, exportSpec);
         }
-        if (tokenStream.getToken() == TOK_NAME &&
-            tokenStream.currentName() == context->names().from)
-        {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt == TOK_NAME && tokenStream.currentName() == context->names().from) {
             MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
             Node moduleSpec = stringLiteral();
             if (!moduleSpec)
                 return null();
 
             if (!MatchOrInsertSemicolon(tokenStream))
                 return null();
@@ -4403,17 +4424,17 @@ Parser<FullParseHandler>::forStatement()
              */
             pc->parsingForInit = true;
             if (tt == TOK_VAR || tt == TOK_CONST) {
                 isForDecl = true;
                 tokenStream.consumeKnownToken(tt);
                 pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
             } else if (tt == TOK_LET) {
                 handler.disableSyntaxParser();
-                (void) tokenStream.getToken();
+                tokenStream.consumeKnownToken(tt);
                 if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt == TOK_LP) {
                     pn1 = letBlock(LetExpresion);
                 } else {
                     isForDecl = true;
                     blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
@@ -4835,17 +4856,21 @@ Parser<ParseHandler>::switchStatement()
     if (!caseList)
         return null();
 
     Node saveBlock = pc->blockNode;
     pc->blockNode = caseList;
 
     bool seenDefault = false;
     TokenKind tt;
-    while ((tt = tokenStream.getToken()) != TOK_RC) {
+    while (true) {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt == TOK_RC)
+            break;
         uint32_t caseBegin = pos().begin;
 
         Node caseExpr;
         switch (tt) {
           case TOK_DEFAULT:
             if (seenDefault) {
                 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
                 return null();
@@ -4855,19 +4880,16 @@ Parser<ParseHandler>::switchStatement()
             break;
 
           case TOK_CASE:
             caseExpr = expr();
             if (!caseExpr)
                 return null();
             break;
 
-          case TOK_ERROR:
-            return null();
-
           default:
             report(ParseError, false, null(), JSMSG_BAD_SWITCH);
             return null();
         }
 
         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
 
         Node body = handler.newStatementList(pc->blockid(), pos());
@@ -5347,17 +5369,19 @@ Parser<ParseHandler>::tryStatement()
     Node innerBlock = statements();
     if (!innerBlock)
         return null();
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
     PopStatementPC(tokenStream, pc);
 
     bool hasUnconditionalCatch = false;
     Node catchList = null();
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
     if (tt == TOK_CATCH) {
         catchList = handler.newList(PNK_CATCH);
         if (!catchList)
             return null();
 
         do {
             Node pnblock;
             BindData<ParseHandler> data(context);
@@ -5390,17 +5414,18 @@ Parser<ParseHandler>::tryStatement()
              * Contrary to ECMA Ed. 3, the catch variable is lexically
              * scoped, not a property of a new Object instance.  This is
              * an intentional change that anticipates ECMA Ed. 4.
              */
             data.initLet(HoistVars, &pc->staticScope->template as<StaticBlockObject>(),
                          JSMSG_TOO_MANY_CATCH_VARS);
             MOZ_ASSERT(data.let.blockObj);
 
-            tt = tokenStream.getToken();
+            if (!tokenStream.getToken(&tt))
+                return null();
             Node catchName;
             switch (tt) {
               case TOK_LB:
               case TOK_LC:
                 catchName = destructuringExpr(&data, tt);
                 if (!catchName)
                     return null();
                 break;
@@ -5451,17 +5476,18 @@ Parser<ParseHandler>::tryStatement()
             if (!catchGuard)
                 hasUnconditionalCatch = true;
 
             if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
                 return null();
             handler.setEndPosition(catchList, pos().end);
             handler.setEndPosition(pnblock, pos().end);
 
-            tt = tokenStream.getToken(TokenStream::Operand);
+            if (!tokenStream.getToken(&tt, TokenStream::Operand))
+                return null();
         } while (tt == TOK_CATCH);
     }
 
     Node finallyBlock = null();
 
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
         if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc))
@@ -5499,17 +5525,20 @@ Parser<ParseHandler>::debuggerStatement(
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statement(bool canHaveDirectives)
 {
     JS_CHECK_RECURSION(context, return null());
 
-    switch (TokenKind tt = tokenStream.getToken(TokenStream::Operand)) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+    switch (tt) {
       case TOK_LC:
         return blockStatement();
 
       case TOK_CONST:
         if (!abortIfSyntaxParser())
             return null();
         // FALL THROUGH
       case TOK_VAR: {
@@ -5564,19 +5593,16 @@ Parser<ParseHandler>::statement(bool can
       case TOK_CATCH:
         report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
         return null();
 
       case TOK_FINALLY:
         report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
-      case TOK_ERROR:
-        return null();
-
       case TOK_STRING:
         if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
             if (!abortIfSyntaxParser())
                 return null();
             if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
                 return null();
         }
         return expressionStatement();
@@ -5739,18 +5765,18 @@ Parser<ParseHandler>::orExpr1()
     Node pn;
     for (;;) {
         pn = unaryExpr();
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
-        TokenKind tok = tokenStream.getToken();
-        if (tok == TOK_ERROR)
+        TokenKind tok;
+        if (!tokenStream.getToken(&tok))
             return null();
         ParseNodeKind pnk;
         if (IsBinaryOpToken(tok, oldParsingForInit)) {
             pnk = BinaryOpTokenKindToParseNodeKind(tok);
         } else {
             tok = TOK_EOF;
             pnk = PNK_LIMIT;
         }
@@ -5807,17 +5833,20 @@ Parser<ParseHandler>::condExpr1()
         return null();
 
     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
     Node elseExpr = assignExpr();
     if (!elseExpr)
         return null();
 
-    tokenStream.getToken(); /* read one token past the end */
+    // 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);
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
 {
     switch (pn->getKind()) {
@@ -5896,17 +5925,19 @@ Parser<ParseHandler>::assignExpr()
     //
     // (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(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
     // primaryExpr().
 
-    TokenKind tt = tokenStream.getToken(TokenStream::Operand);
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
 
     if (tt == TOK_NAME && tokenStream.nextTokenEndsExpr())
         return identifierName();
 
     if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr())
         return newNumber(tokenStream.currentToken());
 
     if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
@@ -5942,19 +5973,19 @@ Parser<ParseHandler>::assignExpr()
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
 
       case TOK_ARROW: {
         tokenStream.seek(start);
         if (!abortIfSyntaxParser())
             return null();
 
-        if (tokenStream.getToken() == TOK_ERROR)
-            return null();
-        tokenStream.ungetToken();
+        TokenKind ignored;
+        if (!tokenStream.peekToken(&ignored))
+            return null();
 
         return functionDef(NullPtr(), Normal, Arrow, NotGenerator);
       }
 
       default:
         MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
         tokenStream.ungetToken();
         return lhs;
@@ -6028,17 +6059,19 @@ Parser<ParseHandler>::unaryOpExpr(ParseN
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr()
 {
     Node pn, pn2;
 
     JS_CHECK_RECURSION(context, return null());
 
-    TokenKind tt = tokenStream.getToken(TokenStream::Operand);
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
     uint32_t begin = pos().begin;
     switch (tt) {
       case TOK_TYPEOF:
         return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin);
       case TOK_VOID:
         return unaryOpExpr(PNK_VOID, JSOP_VOID, begin);
       case TOK_NOT:
         return unaryOpExpr(PNK_NOT, JSOP_NOT, begin);
@@ -6047,17 +6080,19 @@ Parser<ParseHandler>::unaryExpr()
       case TOK_ADD:
         return unaryOpExpr(PNK_POS, JSOP_POS, begin);
       case TOK_SUB:
         return unaryOpExpr(PNK_NEG, JSOP_NEG, begin);
 
       case TOK_INC:
       case TOK_DEC:
       {
-        TokenKind tt2 = tokenStream.getToken(TokenStream::Operand);
+        TokenKind tt2;
+        if (!tokenStream.getToken(&tt2, TokenStream::Operand))
+            return null();
         pn2 = memberExpr(tt2, true);
         if (!pn2)
             return null();
         if (!checkAndMarkAsIncOperand(pn2, tt, true))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
@@ -6075,19 +6110,16 @@ Parser<ParseHandler>::unaryExpr()
             if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
-      case TOK_ERROR:
-        return null();
-
       default:
         pn = memberExpr(tt, true);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
         if (tt == TOK_INC || tt == TOK_DEC) {
@@ -6477,17 +6509,18 @@ Parser<FullParseHandler>::legacyComprehe
         pn2->pn_iflags = JSITER_ENUMERATE;
         if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each))
             pn2->pn_iflags |= JSITER_FOREACH;
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
         uint32_t startYieldOffset = pc->lastYieldOffset;
 
         RootedPropertyName name(context);
-        tt = tokenStream.getToken();
+        if (!tokenStream.getToken(&tt))
+            return null();
         switch (tt) {
           case TOK_LB:
           case TOK_LC:
             pc->inDeclDestructuring = true;
             pn3 = primaryExpr(tt);
             pc->inDeclDestructuring = false;
             if (!pn3)
                 return null();
@@ -6505,18 +6538,16 @@ Parser<FullParseHandler>::legacyComprehe
              */
             pn3 = newBindingNode(name, false);
             if (!pn3)
                 return null();
             break;
 
           default:
             report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
-
-          case TOK_ERROR:
             return null();
         }
 
         bool isForOf;
         if (!matchInOrOf(&isForOf)) {
             report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
             return null();
         }
@@ -7124,17 +7155,20 @@ Parser<ParseHandler>::argumentList(Node 
             }
         }
 #endif
         arg0 = false;
 
         handler.addList(listNode, argNode);
     } while (tokenStream.matchToken(TOK_COMMA));
 
-    if (tokenStream.getToken() != TOK_RP) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return false;
+    if (tt != TOK_RP) {
         report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
         return false;
     }
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 template <typename ParseHandler>
@@ -7148,17 +7182,18 @@ Parser<ParseHandler>::memberExpr(TokenKi
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
         lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
         if (!lhs)
             return null();
 
-        tt = tokenStream.getToken(TokenStream::Operand);
+        if (!tokenStream.getToken(&tt, TokenStream::Operand))
+            return null();
         Node ctorExpr = memberExpr(tt, false);
         if (!ctorExpr)
             return null();
 
         handler.addList(lhs, ctorExpr);
 
         if (tokenStream.matchToken(TOK_LP)) {
             bool isSpread = false;
@@ -7168,21 +7203,25 @@ Parser<ParseHandler>::memberExpr(TokenKi
                 handler.setOp(lhs, JSOP_SPREADNEW);
         }
     } else {
         lhs = primaryExpr(tt);
         if (!lhs)
             return null();
     }
 
-    while ((tt = tokenStream.getToken()) > TOK_EOF) {
+    while (true) {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt == TOK_EOF)
+            break;
+
         Node nextMember;
         if (tt == TOK_DOT) {
-            tt = tokenStream.getToken(TokenStream::KeywordIsName);
-            if (tt == TOK_ERROR)
+            if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
                 return null();
             if (tt == TOK_NAME) {
                 PropertyName *field = tokenStream.currentName();
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
                 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
@@ -7251,18 +7290,16 @@ Parser<ParseHandler>::memberExpr(TokenKi
             handler.setOp(nextMember, op);
         } else {
             tokenStream.ungetToken();
             return lhs;
         }
 
         lhs = nextMember;
     }
-    if (tt == TOK_ERROR)
-        return null();
     return lhs;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName *name)
 {
     return handler.newName(name, pc->blockid(), pos());
@@ -7494,33 +7531,34 @@ Parser<ParseHandler>::objectLiteral()
 
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
     RootedAtom atom(context);
     for (;;) {
-        TokenKind ltok = tokenStream.getToken(TokenStream::KeywordIsName);
+        TokenKind ltok;
+        if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+            return null();
         if (ltok == TOK_RC)
             break;
 
         bool isGenerator = false;
         if (ltok == TOK_MUL) {
             isGenerator = true;
-            ltok = tokenStream.getToken(TokenStream::KeywordIsName);
+            if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+                return null();
         }
 
         atom = nullptr;
 
         JSOp op = JSOP_INITPROP;
         Node propname;
         switch (ltok) {
-          case TOK_ERROR:
-            return null();
           case TOK_NUMBER:
             atom = DoubleToAtom(context, tokenStream.currentToken().number());
             if (!atom)
                 return null();
             propname = newNumber(tokenStream.currentToken());
             break;
 
           case TOK_LB: {
@@ -7548,17 +7586,19 @@ Parser<ParseHandler>::objectLiteral()
                 propname = handler.newIdentifier(atom, pos());
                 if (!propname)
                     return null();
                 break;
             }
 
             // We have parsed |get| or |set|. Look for an accessor property
             // name next.
-            TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+                return null();
             if (tt == TOK_NAME) {
                 atom = tokenStream.currentName();
                 propname = newName(atom->asPropertyName());
                 if (!propname)
                     return null();
             } else if (tt == TOK_STRING) {
                 atom = tokenStream.currentToken().atom();
 
@@ -7616,18 +7656,18 @@ Parser<ParseHandler>::objectLiteral()
           }
 
           default:
             report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
             return null();
         }
 
         if (op == JSOP_INITPROP) {
-            TokenKind tt = tokenStream.getToken();
-            if (tt == TOK_ERROR)
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
                 return null();
 
             if (tt == TOK_COLON) {
                 if (isGenerator) {
                     report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
                     return null();
                 }
 
@@ -7694,17 +7734,19 @@ Parser<ParseHandler>::objectLiteral()
         } else {
             /* NB: Getter function in { get x(){} } is unnamed. */
             if (!methodDefinition(literal, propname, op == JSOP_INITPROP_GETTER ? Getter : Setter,
                                   Expression, NotGenerator, op)) {
                 return null();
             }
         }
 
-        TokenKind tt = tokenStream.getToken();
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return null();
         if (tt == TOK_RC)
             break;
         if (tt != TOK_COMMA) {
             report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
             return null();
         }
     }
 
@@ -7806,27 +7848,25 @@ Parser<ParseHandler>::primaryExpr(TokenK
 
       case TOK_TRIPLEDOT: {
         TokenKind next;
 
         // This isn't valid expression syntax, but it's valid in an arrow
         // function as a trailing rest param: `(a, b, ...rest) => body`.  Check
         // for a name, closing parenthesis, and arrow, and allow it only if all
         // are present.
-        next = tokenStream.getToken();
-        if (next == TOK_ERROR)
+        if (!tokenStream.getToken(&next))
             return null();
         if (next != TOK_NAME) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "rest argument name", TokenKindToDesc(next));
             return null();
         }
 
-        next = tokenStream.getToken();
-        if (next == TOK_ERROR)
+        if (!tokenStream.getToken(&next))
             return null();
         if (next != TOK_RP) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "closing parenthesis", TokenKindToDesc(next));
             return null();
         }
 
         if (!tokenStream.peekToken(&next))
@@ -7838,20 +7878,16 @@ Parser<ParseHandler>::primaryExpr(TokenK
         }
 
         tokenStream.ungetToken();  // put back right paren
 
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
 
-      case TOK_ERROR:
-        /* The scanner or one of its subroutines reported the error. */
-        return null();
-
       default:
       unexpected_token:
         report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                "expression", TokenKindToDesc(tt));
         return null();
     }
 }
 
@@ -7890,17 +7926,20 @@ Parser<ParseHandler>::parenExprOrGenerat
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
-        if (tokenStream.getToken() != TOK_RP) {
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt != TOK_RP) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         handler.setEndPosition(pn, pos().end);
         handler.setInParens(pn);
         return pn;
     }
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -363,29 +363,32 @@ class MOZ_STACK_CLASS TokenStream
         Operand,        // Looking for an operand, not an operator.  In
                         //   practice, this means that when '/' is seen,
                         //   we look for a regexp instead of just returning
                         //   TOK_DIV.
         KeywordIsName,  // Treat keywords as names by returning TOK_NAME.
         TemplateTail,   // Treat next characters as part of a template string
     };
 
-    // Get the next token from the stream, make it the current token, and
-    // return its kind.
-    TokenKind getToken(Modifier modifier = None) {
+    // Advance to the next token.  If the token stream encountered an error,
+    // return false.  Otherwise return true and store the token kind in |*ttp|.
+    bool getToken(TokenKind *ttp, Modifier modifier = None) {
         // Check for a pushed-back token resulting from mismatching lookahead.
         if (lookahead != 0) {
             lookahead--;
             cursor = (cursor + 1) & ntokensMask;
             TokenKind tt = currentToken().type;
             MOZ_ASSERT(tt != TOK_EOL);
-            return tt;
+            *ttp = tt;
+            return tt != TOK_ERROR;
         }
 
-        return getTokenInternal(modifier);
+        TokenKind tt = getTokenInternal(modifier);
+        *ttp = tt;
+        return tt != TOK_ERROR;
     }
 
     // Push the last scanned token back into the stream.
     void ungetToken() {
         MOZ_ASSERT(lookahead < maxLookahead);
         lookahead++;
         cursor = (cursor - 1) & ntokensMask;
     }
@@ -427,38 +430,51 @@ class MOZ_STACK_CLASS TokenStream
 
         // The above check misses two cases where we don't have to return
         // TOK_EOL.
         // - The next token starts on the same line, but is a multi-line token.
         // - The next token starts on the same line, but lookahead==2 and there
         //   is a newline between the next token and the one after that.
         // The following test is somewhat expensive but gets these cases (and
         // all others) right.
-        (void)getToken(modifier);
+        TokenKind tmp;
+        if (!getToken(&tmp, modifier))
+            return TOK_ERROR;
         const Token &next = currentToken();
         ungetToken();
         return srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
                ? next.type
                : TOK_EOL;
     }
 
     // Get the next token from the stream if its kind is |tt|.
     bool matchToken(TokenKind tt, Modifier modifier = None) {
-        if (getToken(modifier) == tt)
+        TokenKind token;
+        if (!getToken(&token, modifier)) {
+            ungetToken();
+            return false;
+        }
+        if (token == tt)
             return true;
         ungetToken();
         return false;
     }
 
     void consumeKnownToken(TokenKind tt) {
+        MOZ_ASSERT(lookahead != 0);
         JS_ALWAYS_TRUE(matchToken(tt));
     }
 
     bool matchContextualKeyword(Handle<PropertyName*> keyword) {
-        if (getToken() == TOK_NAME && currentToken().name() == keyword)
+        TokenKind token;
+        if (!getToken(&token)) {
+            ungetToken();
+            return false;
+        }
+        if (token == TOK_NAME && currentToken().name() == keyword)
             return true;
         ungetToken();
         return false;
     }
 
     bool nextTokenEndsExpr() {
         TokenKind tt;
         if (!peekToken(&tt))
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -931,41 +931,43 @@ js::FindBody(JSContext *cx, HandleFuncti
         return false;
 
     const mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange();
     TokenStream ts(cx, options, srcChars.start().get(), srcChars.length(), nullptr);
     int nest = 0;
     bool onward = true;
     // Skip arguments list.
     do {
-        switch (ts.getToken()) {
+        TokenKind tt;
+        if (!ts.getToken(&tt))
+            return false;
+        switch (tt) {
           case TOK_NAME:
           case TOK_YIELD:
             if (nest == 0)
                 onward = false;
             break;
           case TOK_LP:
             nest++;
             break;
           case TOK_RP:
             if (--nest == 0)
                 onward = false;
             break;
-          case TOK_ERROR:
-            // Must be memory.
-            return false;
           default:
             break;
         }
     } while (onward);
-    TokenKind tt = ts.getToken();
-    if (tt == TOK_ARROW)
-        tt = ts.getToken();
-    if (tt == TOK_ERROR)
+    TokenKind tt;
+    if (!ts.getToken(&tt))
         return false;
+    if (tt == TOK_ARROW) {
+        if (!ts.getToken(&tt))
+            return false;
+    }
     bool braced = tt == TOK_LC;
     MOZ_ASSERT_IF(fun->isExprClosure(), !braced);
     *bodyStart = ts.currentToken().pos.begin;
     if (braced)
         *bodyStart += 1;
     mozilla::RangedPtr<const char16_t> end = srcChars.end();
     if (end[-1] == '}') {
         end--;
@@ -1655,22 +1657,19 @@ js_fun_bind(JSContext *cx, HandleObject 
     return funobj;
 }
 
 /*
  * Report "malformed formal parameter" iff no illegal char or similar scanner
  * error was already reported.
  */
 static bool
-OnBadFormal(JSContext *cx, TokenKind tt)
+OnBadFormal(JSContext *cx)
 {
-    if (tt != TOK_ERROR)
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
-    else
-        MOZ_ASSERT(cx->isExceptionPending());
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
     return false;
 }
 
 const JSFunctionSpec js::function_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,   fun_toSource,   0,0),
 #endif
     JS_FN(js_toString_str,   fun_toString,   0,0),
@@ -1801,60 +1800,61 @@ FunctionConstructor(JSContext *cx, unsig
          * here (duplicate argument names, etc.) will be detected when we
          * compile the function body.
          */
         TokenStream ts(cx, options, collected_args.get(), args_length,
                        /* strictModeGetter = */ nullptr);
         bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator;
 
         /* The argument string may be empty or contain no tokens. */
-        TokenKind tt = ts.getToken();
+        TokenKind tt;
+        if (!ts.getToken(&tt))
+            return false;
         if (tt != TOK_EOF) {
             for (;;) {
-                /*
-                 * Check that it's a name.  This also implicitly guards against
-                 * TOK_ERROR, which was already reported.
-                 */
+                /* Check that it's a name. */
                 if (hasRest) {
                     ts.reportError(JSMSG_PARAMETER_AFTER_REST);
                     return false;
                 }
 
                 if (tt == TOK_YIELD && yieldIsValidName)
                     tt = TOK_NAME;
 
                 if (tt != TOK_NAME) {
                     if (tt == TOK_TRIPLEDOT) {
                         hasRest = true;
-                        tt = ts.getToken();
+                        if (!ts.getToken(&tt))
+                            return false;
                         if (tt == TOK_YIELD && yieldIsValidName)
                             tt = TOK_NAME;
                         if (tt != TOK_NAME) {
-                            if (tt != TOK_ERROR)
-                                ts.reportError(JSMSG_NO_REST_NAME);
+                            ts.reportError(JSMSG_NO_REST_NAME);
                             return false;
                         }
                     } else {
-                        return OnBadFormal(cx, tt);
+                        return OnBadFormal(cx);
                     }
                 }
 
                 if (!formals.append(ts.currentName()))
                     return false;
 
                 /*
                  * Get the next token.  Stop on end of stream.  Otherwise
                  * insist on a comma, get another name, and iterate.
                  */
-                tt = ts.getToken();
+                if (!ts.getToken(&tt))
+                    return false;
                 if (tt == TOK_EOF)
                     break;
                 if (tt != TOK_COMMA)
-                    return OnBadFormal(cx, tt);
-                tt = ts.getToken();
+                    return OnBadFormal(cx);
+                if (!ts.getToken(&tt))
+                    return false;
             }
         }
     }
 
 #ifdef DEBUG
     for (unsigned i = 0; i < formals.length(); ++i) {
         JSString *str = formals[i];
         MOZ_ASSERT(str->asAtom().asPropertyName() == formals[i]);