summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-06-08 14:51:46 -0400
committerGaming4JC <g4jc@hyperbola.info>2019-07-18 22:38:14 -0400
commit7ecc50d90d13690d610f26d0056a326e52bc834c (patch)
tree18e5e9248a8011e44de84cb8b73f77f9027d0dd6
parent987d6726f3c64d1cc510acba1de08deb5e0a7702 (diff)
downloadUXP-7ecc50d90d13690d610f26d0056a326e52bc834c.tar
UXP-7ecc50d90d13690d610f26d0056a326e52bc834c.tar.gz
UXP-7ecc50d90d13690d610f26d0056a326e52bc834c.tar.lz
UXP-7ecc50d90d13690d610f26d0056a326e52bc834c.tar.xz
UXP-7ecc50d90d13690d610f26d0056a326e52bc834c.zip
1317379 - Disallow generator functions and async functions as direct children of if/else.
-rw-r--r--js/src/frontend/Parser.cpp83
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/forbidden-as-consequent.js14
-rw-r--r--js/src/tests/ecma_6/Generators/forbidden-as-consequent.js11
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);