diff options
-rw-r--r-- | js/src/frontend/Parser.cpp | 83 | ||||
-rw-r--r-- | js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js | 14 | ||||
-rw-r--r-- | js/src/tests/ecma_6/Generators/forbidden-as-consequent.js | 11 |
3 files changed, 87 insertions, 21 deletions
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 8f52f1d27..a7b1f3a14 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -5248,33 +5248,74 @@ Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling) if (!tokenStream.peekToken(&next, TokenStream::Operand)) return null(); - if (next == TOK_FUNCTION) { - // Annex B.3.4 says that unbraced function declarations under if/else - // in non-strict code act as if they were braced. That is, - // |if (x) function f() {}| is parsed as |if (x) { function f() {} }|. - if (!pc->sc()->strict()) { - tokenStream.consumeKnownToken(next, TokenStream::Operand); + // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in + // non-strict code act as if they were braced: |if (x) function f() {}| + // parses as |if (x) { function f() {} }|. + // + // Careful! FunctionDeclaration doesn't include generators or async + // functions. + if (next == TOK_NAME && + !tokenStream.nextNameContainsEscape() && + tokenStream.nextName() == context->names().async) + { + tokenStream.consumeKnownToken(next, TokenStream::Operand); - ParseContext::Statement stmt(pc, StatementKind::Block); - ParseContext::Scope scope(this); - if (!scope.init(pc)) - return null(); + // Peek only on the same line: ExpressionStatement's lookahead + // restriction is phrased as + // + // [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }] + // + // meaning that code like this is valid: + // + // if (true) + // async // ASI opportunity + // function clownshoes() {} + TokenKind maybeFunction; + if (!tokenStream.peekTokenSameLine(&maybeFunction)) + return null(); - TokenPos funcPos = pos(); - Node fun = functionStmt(pos().begin, yieldHandling, NameRequired); - if (!fun) - return null(); + if (maybeFunction == TOK_FUNCTION) { + error(JSMSG_FORBIDDEN_AS_STATEMENT, "async function declarations"); + return null(); + } - Node block = handler.newStatementList(funcPos); - if (!block) - return null(); + // Otherwise this |async| begins an ExpressionStatement. + tokenStream.ungetToken(); + } else if (next == TOK_FUNCTION) { + tokenStream.consumeKnownToken(next, TokenStream::Operand); - handler.addStatementToList(block, fun); - return finishLexicalScope(scope, block); + // Parser::statement would handle this, but as this function handles + // every other error case, it seems best to handle this. + if (pc->sc()->strict()) { + error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); + return null(); + } + + TokenKind maybeStar; + if (!tokenStream.peekToken(&maybeStar)) + return null(); + + if (maybeStar == TOK_MUL) { + error(JSMSG_FORBIDDEN_AS_STATEMENT, "generator declarations"); + return null(); } - // Function declarations are a syntax error in strict mode code. - // Parser::statement reports that error. + ParseContext::Statement stmt(pc, StatementKind::Block); + ParseContext::Scope scope(this); + if (!scope.init(pc)) + return null(); + + TokenPos funcPos = pos(); + Node fun = functionStmt(pos().begin, yieldHandling, NameRequired); + if (!fun) + return null(); + + Node block = handler.newStatementList(funcPos); + if (!block) + return null(); + + handler.addStatementToList(block, fun); + return finishLexicalScope(scope, block); } return statement(yieldHandling); diff --git a/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js b/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js new file mode 100644 index 000000000..656ed46de --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js @@ -0,0 +1,14 @@ +/* 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/. */ + +assertThrowsInstanceOf(() => eval("if (1) async function foo() {}"), + SyntaxError); +assertThrowsInstanceOf(() => eval("'use strict'; if (1) async function foo() {}"), + SyntaxError); + +var async = 42; +assertEq(eval("if (1) async \n function foo() {}"), 42); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js b/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js new file mode 100644 index 000000000..13647e154 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/forbidden-as-consequent.js @@ -0,0 +1,11 @@ +/* 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/. */ + +assertThrowsInstanceOf(() => eval("if (1) function* foo() {}"), + SyntaxError); +assertThrowsInstanceOf(() => eval("'use strict'; if (1) function* foo() {}"), + SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(true, true); |