mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 09:45:41 +00:00
Bug 1319416 - Substantially rejigger token lookahead and modifier exceptions to function more like the spec. r=arai
This commit is contained in:
parent
724cfcb0a6
commit
7233b2770b
@ -2542,11 +2542,11 @@ Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
|
||||
|
||||
// Skip prelude.
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
if (asyncKind == AsyncFunction) {
|
||||
MOZ_ASSERT(tt == TOK_ASYNC);
|
||||
if (!tokenStream.getToken(&tt))
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
}
|
||||
MOZ_ASSERT(tt == TOK_FUNCTION);
|
||||
@ -2863,10 +2863,10 @@ ParserBase::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
||||
*/
|
||||
template <class ParseHandler, typename CharT>
|
||||
bool
|
||||
Parser<ParseHandler, CharT>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
|
||||
Parser<ParseHandler, CharT>::matchOrInsertSemicolonHelper()
|
||||
{
|
||||
TokenKind tt = TOK_EOF;
|
||||
if (!tokenStream.peekTokenSameLine(&tt, modifier))
|
||||
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
|
||||
return false;
|
||||
if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
|
||||
/*
|
||||
@ -2891,30 +2891,26 @@ Parser<ParseHandler, CharT>::matchOrInsertSemicolonHelper(TokenStream::Modifier
|
||||
}
|
||||
|
||||
/* Advance the scanner for proper error location reporting. */
|
||||
tokenStream.consumeKnownToken(tt, modifier);
|
||||
tokenStream.consumeKnownToken(tt, TokenStream::Operand);
|
||||
error(JSMSG_SEMI_BEFORE_STMNT);
|
||||
return false;
|
||||
}
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
|
||||
return false;
|
||||
if (!matched && modifier == TokenStream::None)
|
||||
tokenStream.addModifierException(TokenStream::OperandIsNone);
|
||||
return true;
|
||||
return tokenStream.matchToken(&matched, TOK_SEMI, TokenStream::Operand);
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
bool
|
||||
Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterExpression()
|
||||
{
|
||||
return matchOrInsertSemicolonHelper(TokenStream::None);
|
||||
return matchOrInsertSemicolonHelper();
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
bool
|
||||
Parser<ParseHandler, CharT>::matchOrInsertSemicolonAfterNonExpression()
|
||||
{
|
||||
return matchOrInsertSemicolonHelper(TokenStream::Operand);
|
||||
return matchOrInsertSemicolonHelper();
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
@ -3304,7 +3300,7 @@ Parser<ParseHandler, CharT>::addExprAndGetNextTemplStrToken(YieldHandling yieldH
|
||||
handler.addList(nodeList, pn);
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return false;
|
||||
if (tt != TOK_RC) {
|
||||
error(JSMSG_TEMPLSTR_UNTERM_EXPR);
|
||||
@ -4191,10 +4187,12 @@ typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::condition(InHandling inHandling, YieldHandling yieldHandling)
|
||||
{
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
|
||||
|
||||
Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited);
|
||||
if (!pn)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
|
||||
|
||||
/* Check for (a = b) and warn about possible (a == b) mistype. */
|
||||
if (handler.isUnparenthesizedAssignment(pn)) {
|
||||
@ -4580,7 +4578,6 @@ Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHand
|
||||
return null();
|
||||
|
||||
uint32_t index = 0;
|
||||
TokenStream::Modifier modifier = TokenStream::Operand;
|
||||
for (; ; index++) {
|
||||
if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
|
||||
error(JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
@ -4588,7 +4585,7 @@ Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHand
|
||||
}
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
||||
if (tt == TOK_RB) {
|
||||
@ -4603,7 +4600,7 @@ Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHand
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
||||
Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt);
|
||||
@ -4635,10 +4632,9 @@ Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHand
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA))
|
||||
return null();
|
||||
if (!matched) {
|
||||
modifier = TokenStream::None;
|
||||
if (!matched)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
error(JSMSG_REST_WITH_COMMA);
|
||||
return null();
|
||||
@ -4646,7 +4642,7 @@ Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHand
|
||||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, TokenStream::None,
|
||||
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
|
||||
JSMSG_BRACKET_OPENED, begin));
|
||||
|
||||
@ -4766,7 +4762,7 @@ Parser<ParseHandler, CharT>::declarationPattern(Node decl, DeclarationKind declK
|
||||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_ASSIGN, TokenStream::Operand, JSMSG_BAD_DESTRUCT_DECL);
|
||||
|
||||
Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
|
||||
yieldHandling, TripledotProhibited);
|
||||
@ -4775,21 +4771,6 @@ Parser<ParseHandler, CharT>::declarationPattern(Node decl, DeclarationKind declK
|
||||
|
||||
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.
|
||||
// Similar to the case in initializerInNameDeclaration(), we need to
|
||||
// peek at the next token when assignExpr() is a lazily parsed arrow
|
||||
// function.
|
||||
TokenKind ignored;
|
||||
if (!tokenStream.peekToken(&ignored))
|
||||
return null();
|
||||
tokenStream.addModifierException(TokenStream::OperandIsNone);
|
||||
}
|
||||
|
||||
return handler.newAssignment(PNK_ASSIGN, pattern, init);
|
||||
}
|
||||
|
||||
@ -4816,64 +4797,39 @@ Parser<ParseHandler, CharT>::initializerInNameDeclaration(Node decl, Node bindin
|
||||
|
||||
handler.checkAndSetIsDirectRHSAnonFunction(initializer);
|
||||
|
||||
if (forHeadKind) {
|
||||
if (initialDeclaration) {
|
||||
bool isForIn, isForOf;
|
||||
if (!matchInOrOf(&isForIn, &isForOf))
|
||||
return false;
|
||||
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:
|
||||
//
|
||||
// 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;
|
||||
// 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 (*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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4920,8 +4876,6 @@ Parser<ParseHandler, CharT>::declarationName(Node decl, DeclarationKind declKind
|
||||
return null();
|
||||
}
|
||||
} else {
|
||||
tokenStream.addModifierException(TokenStream::NoneIsOperand);
|
||||
|
||||
if (initialDeclaration && forHeadKind) {
|
||||
bool isForIn, isForOf;
|
||||
if (!matchInOrOf(&isForIn, &isForOf))
|
||||
@ -4990,7 +4944,7 @@ Parser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
|
||||
if (!decl)
|
||||
return null();
|
||||
|
||||
bool matched;
|
||||
bool moreDeclarations;
|
||||
bool initialDeclaration = true;
|
||||
do {
|
||||
MOZ_ASSERT_IF(!initialDeclaration && forHeadKind,
|
||||
@ -5010,14 +4964,16 @@ Parser<ParseHandler, CharT>::declarationList(YieldHandling yieldHandling,
|
||||
|
||||
handler.addList(decl, binding);
|
||||
|
||||
// If we have a for-in/of loop, the above call matches the entirety
|
||||
// of the loop head (up to the closing parenthesis).
|
||||
if (forHeadKind && *forHeadKind != PNK_FORHEAD)
|
||||
break;
|
||||
|
||||
initialDeclaration = false;
|
||||
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA))
|
||||
if (!tokenStream.matchToken(&moreDeclarations, TOK_COMMA, TokenStream::Operand))
|
||||
return null();
|
||||
} while (matched);
|
||||
} while (moreDeclarations);
|
||||
|
||||
return decl;
|
||||
}
|
||||
@ -6050,7 +6006,7 @@ bool
|
||||
Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
|
||||
{
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return false;
|
||||
|
||||
*isForInp = tt == TOK_IN;
|
||||
@ -6065,7 +6021,7 @@ Parser<ParseHandler, CharT>::matchInOrOf(bool* isForInp, bool* isForOfp)
|
||||
template <class ParseHandler, typename CharT>
|
||||
bool
|
||||
Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
|
||||
IteratorKind iterKind,
|
||||
IteratorKind iterKind,
|
||||
ParseNodeKind* forHeadKind,
|
||||
Node* forInitialPart,
|
||||
Maybe<ParseContext::Scope>& forLoopLexicalScope,
|
||||
@ -6157,14 +6113,12 @@ Parser<ParseHandler, CharT>::forHeadStart(YieldHandling yieldHandling,
|
||||
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. Allow the Operand
|
||||
// modifier when regetting: Operand must be used to examine the ';' in
|
||||
// |for (;|, and our caller handles this case and that.
|
||||
// the init expression; the caller handles the rest.
|
||||
if (!isForIn && !isForOf) {
|
||||
if (!possibleError.checkForExpressionError())
|
||||
return false;
|
||||
|
||||
*forHeadKind = PNK_FORHEAD;
|
||||
tokenStream.addModifierException(TokenStream::OperandIsNone);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6326,18 +6280,15 @@ Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
|
||||
return null();
|
||||
|
||||
Node test;
|
||||
TokenStream::Modifier mod;
|
||||
if (tt == TOK_SEMI) {
|
||||
test = null();
|
||||
mod = TokenStream::Operand;
|
||||
} else {
|
||||
test = expr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!test)
|
||||
return null();
|
||||
mod = TokenStream::None;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_SEMI, mod, JSMSG_SEMI_AFTER_FOR_COND);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_SEMI, TokenStream::Operand, JSMSG_SEMI_AFTER_FOR_COND);
|
||||
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
@ -6345,15 +6296,13 @@ Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
|
||||
Node update;
|
||||
if (tt == TOK_RP) {
|
||||
update = null();
|
||||
mod = TokenStream::Operand;
|
||||
} else {
|
||||
update = expr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!update)
|
||||
return null();
|
||||
mod = TokenStream::None;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, mod, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
forHead = handler.newForHead(init, test, update, headPos);
|
||||
@ -6376,9 +6325,9 @@ Parser<ParseHandler, CharT>::forStatement(YieldHandling yieldHandling)
|
||||
}
|
||||
|
||||
// Parser::declaration consumed everything up to the closing ')'. That
|
||||
// token follows an {Assignment,}Expression, so the next token must be
|
||||
// consumed as if an operator continued the expression, i.e. as None.
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::None, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
// token follows an {Assignment,}Expression and so must be interpreted
|
||||
// as an operand to be consistent with normal expression tokenizing.
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
forHead = handler.newForInOrOfHead(headKind, target, iteratedExpr, headPos);
|
||||
@ -6413,7 +6362,7 @@ Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
|
||||
if (!discriminant)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_SWITCH);
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
|
||||
|
||||
ParseContext::Statement stmt(pc, StatementKind::Switch);
|
||||
@ -6456,7 +6405,7 @@ Parser<ParseHandler, CharT>::switchStatement(YieldHandling yieldHandling)
|
||||
return null();
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_COLON, TokenStream::Operand, JSMSG_COLON_AFTER_CASE);
|
||||
|
||||
Node body = handler.newStatementList(pos());
|
||||
if (!body)
|
||||
@ -6726,10 +6675,12 @@ Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
|
||||
|
||||
Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited);
|
||||
if (!objectExpr)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_WITH);
|
||||
|
||||
Node innerBlock;
|
||||
{
|
||||
@ -6959,7 +6910,7 @@ Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
|
||||
* catchguard syntax.
|
||||
*/
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_IF))
|
||||
if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
|
||||
return null();
|
||||
if (matched) {
|
||||
catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
|
||||
@ -6967,7 +6918,7 @@ Parser<ParseHandler, CharT>::tryStatement(YieldHandling yieldHandling)
|
||||
return null();
|
||||
}
|
||||
#endif
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
|
||||
|
||||
@ -7794,7 +7745,7 @@ Parser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHand
|
||||
return null();
|
||||
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA))
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
|
||||
return null();
|
||||
if (!matched)
|
||||
return pn;
|
||||
@ -7824,7 +7775,6 @@ Parser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHand
|
||||
}
|
||||
|
||||
tokenStream.ungetToken(); // put back right paren
|
||||
tokenStream.addModifierException(TokenStream::NoneIsOperand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -7849,7 +7799,7 @@ Parser<ParseHandler, CharT>::expr(InHandling inHandling, YieldHandling yieldHand
|
||||
|
||||
handler.addList(seq, pn);
|
||||
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA))
|
||||
if (!tokenStream.matchToken(&matched, TOK_COMMA, TokenStream::Operand))
|
||||
return null();
|
||||
if (!matched)
|
||||
break;
|
||||
@ -7978,7 +7928,11 @@ Parser<ParseHandler, CharT>::orExpr(InHandling inHandling, YieldHandling yieldHa
|
||||
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 the next token
|
||||
// modifier can be Operand.
|
||||
tokenStream.ungetToken();
|
||||
tokenStream.addModifierException(TokenStream::OperandIsNone);
|
||||
|
||||
MOZ_ASSERT(depth == 0);
|
||||
return pn;
|
||||
@ -8153,13 +8107,6 @@ Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yie
|
||||
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);
|
||||
|
||||
@ -8190,42 +8137,8 @@ Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yie
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
Node arrowFunc = functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr,
|
||||
Arrow, NotGenerator, 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;
|
||||
return functionDefinition(pn, toStringStart, inHandling, yieldHandling, nullptr,
|
||||
Arrow, NotGenerator, asyncKind);
|
||||
}
|
||||
|
||||
default:
|
||||
@ -8236,7 +8149,9 @@ Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yie
|
||||
} else {
|
||||
possibleErrorInner.transferErrorsTo(possibleError);
|
||||
}
|
||||
|
||||
tokenStream.ungetToken();
|
||||
tokenStream.addModifierException(TokenStream::OperandIsNone);
|
||||
return lhs;
|
||||
}
|
||||
|
||||
@ -8536,7 +8451,7 @@ Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
|
||||
if (!comp)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
|
||||
|
||||
uint32_t end = pos().end;
|
||||
handler.setBeginPosition(comp, begin);
|
||||
@ -8605,7 +8520,7 @@ Parser<ParseHandler, CharT>::comprehensionFor(GeneratorKind comprehensionKind)
|
||||
if (!rhs)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
|
||||
@ -8654,7 +8569,7 @@ Parser<ParseHandler, CharT>::comprehensionIf(GeneratorKind comprehensionKind)
|
||||
Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
if (!cond)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
|
||||
|
||||
/* Check for (a = b) and warn about possible (a == b) mistype. */
|
||||
if (handler.isUnparenthesizedAssignment(cond)) {
|
||||
@ -8735,7 +8650,7 @@ Parser<ParseHandler, CharT>::arrayComprehension(uint32_t begin)
|
||||
if (!inner)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
|
||||
|
||||
Node comp = handler.newList(PNK_ARRAYCOMP, inner);
|
||||
if (!comp)
|
||||
@ -8945,7 +8860,7 @@ Parser<ParseHandler, CharT>::memberExpr(YieldHandling yieldHandling,
|
||||
if (!propExpr)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
|
||||
|
||||
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
|
||||
error(JSMSG_BAD_SUPERPROP, "member");
|
||||
@ -9451,9 +9366,7 @@ Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
|
||||
} else {
|
||||
tokenStream.ungetToken();
|
||||
|
||||
uint32_t index = 0;
|
||||
TokenStream::Modifier modifier = TokenStream::Operand;
|
||||
for (; ; index++) {
|
||||
for (uint32_t index = 0; ; index++) {
|
||||
if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) {
|
||||
error(JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
return null();
|
||||
@ -9469,7 +9382,10 @@ Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
|
||||
tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
|
||||
if (!handler.addElision(literal, pos()))
|
||||
return null();
|
||||
} else if (tt == TOK_TRIPLEDOT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
@ -9510,24 +9426,21 @@ Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
|
||||
handler.addArrayElement(literal, element);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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_WITH_REPORT(TOK_RB, modifier,
|
||||
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, TokenStream::Operand,
|
||||
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
|
||||
JSMSG_BRACKET_OPENED, begin));
|
||||
}
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
}
|
||||
@ -9738,6 +9651,8 @@ Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling,
|
||||
const Maybe<DeclarationKind>& maybeDecl,
|
||||
Node literal)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
if (maybeDecl) {
|
||||
@ -10117,7 +10032,7 @@ Parser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
|
||||
if (next == TOK_RP) {
|
||||
// Not valid expression syntax, but this is valid in an arrow function
|
||||
// with no params: `() => body`.
|
||||
tokenStream.consumeKnownToken(next, TokenStream::Operand);
|
||||
tokenStream.consumeKnownToken(TOK_RP, TokenStream::Operand);
|
||||
|
||||
if (!tokenStream.peekToken(&next))
|
||||
return null();
|
||||
@ -10142,7 +10057,7 @@ Parser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
|
||||
Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
|
||||
if (!expr)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
|
||||
return handler.parenthesize(expr);
|
||||
}
|
||||
|
||||
|
@ -839,6 +839,8 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
|
||||
|
||||
bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
|
||||
|
||||
// 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);
|
||||
@ -866,7 +868,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
|
||||
bool finishFunction(bool isStandaloneFunction = false);
|
||||
bool leaveInnerFunction(ParseContext* outerpc);
|
||||
|
||||
bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier);
|
||||
bool matchOrInsertSemicolonHelper();
|
||||
bool matchOrInsertSemicolonAfterExpression();
|
||||
bool matchOrInsertSemicolonAfterNonExpression();
|
||||
|
||||
|
@ -381,8 +381,16 @@ class TokenStreamAnyChars: public ErrorReporter
|
||||
void addModifierException(ModifierException modifierException) {
|
||||
#ifdef DEBUG
|
||||
const Token& next = nextToken();
|
||||
if (next.modifierException == NoneIsOperand)
|
||||
{
|
||||
|
||||
// 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) {
|
||||
// Token after yield expression without operand already has
|
||||
// NoneIsOperand exception.
|
||||
MOZ_ASSERT(modifierException == OperandIsNone);
|
||||
@ -885,7 +893,15 @@ class MOZ_STACK_CLASS TokenStream final : public TokenStreamAnyChars
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,67 @@
|
||||
/* 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/. */
|
||||
|
||||
// Grab-bag of ArrowFunctions with block bodies appearing in contexts where the
|
||||
// subsequent token-examination must use the Operand modifier to avoid an
|
||||
// assertion.
|
||||
|
||||
assertEq(`x ${a => {}} z`, "x a => {} z");
|
||||
|
||||
for (; a => {}; )
|
||||
break;
|
||||
|
||||
for (; ; a => {})
|
||||
break;
|
||||
|
||||
switch (1)
|
||||
{
|
||||
case a => {}:
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Catch guards are non-standard, so ignore a syntax error.
|
||||
eval(`try
|
||||
{
|
||||
}
|
||||
catch (x if a => {})
|
||||
{
|
||||
}`);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
assertEq(e instanceof SyntaxError, true,
|
||||
"should only have thrown SyntaxError, instead got " + e);
|
||||
}
|
||||
|
||||
assertEq(0[a => {}], undefined);
|
||||
|
||||
class Y {};
|
||||
class X extends Y { constructor() { super[a => {}](); } };
|
||||
|
||||
if (a => {})
|
||||
assertEq(true, true);
|
||||
else
|
||||
assertEq(true, false);
|
||||
|
||||
switch (a => {})
|
||||
{
|
||||
}
|
||||
|
||||
with (a => {});
|
||||
|
||||
assertEq(typeof (a => {}), "function");
|
||||
|
||||
for (var x in y => {})
|
||||
continue;
|
||||
|
||||
var z = { x: 0 ? 1 : async a => {} };
|
||||
assertEq(typeof z.x, "function");
|
||||
|
||||
var q = 0 ? 1 : async () => {};
|
||||
assertEq(typeof q, "function");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
@ -0,0 +1,15 @@
|
||||
/* 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);
|
Loading…
Reference in New Issue
Block a user