summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/src/frontend/Parser.cpp89
-rw-r--r--js/src/frontend/Parser.h4
-rw-r--r--js/src/js.msg1
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/await-error.js16
4 files changed, 77 insertions, 33 deletions
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 7d923420a..3c509465f 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2631,39 +2631,62 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
/*
* WARNING: Do not call this function directly.
- * Call either MatchOrInsertSemicolonAfterExpression or
- * MatchOrInsertSemicolonAfterNonExpression instead, depending on context.
+ * Call either matchOrInsertSemicolonAfterExpression or
+ * matchOrInsertSemicolonAfterNonExpression instead, depending on context.
*/
-static bool
-MatchOrInsertSemicolonHelper(TokenStream& ts, TokenStream::Modifier modifier)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifier)
{
TokenKind tt = TOK_EOF;
- if (!ts.peekTokenSameLine(&tt, modifier))
+ 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();
+ * ^
+ * |
+ * tried to insert semicolon here
+ *
+ * Detect this situation and throw an understandable error. Otherwise
+ * we'd throw a confusing "missing ; before statement" error.
+ */
+ if (!pc->isAsync() &&
+ 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. */
- ts.consumeKnownToken(tt, modifier);
- ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
+ tokenStream.consumeKnownToken(tt, modifier);
+ error(JSMSG_SEMI_BEFORE_STMNT);
return false;
}
bool matched;
- if (!ts.matchToken(&matched, TOK_SEMI, modifier))
+ if (!tokenStream.matchToken(&matched, TOK_SEMI, modifier))
return false;
if (!matched && modifier == TokenStream::None)
- ts.addModifierException(TokenStream::OperandIsNone);
+ tokenStream.addModifierException(TokenStream::OperandIsNone);
return true;
}
-static bool
-MatchOrInsertSemicolonAfterExpression(TokenStream& ts)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonAfterExpression()
{
- return MatchOrInsertSemicolonHelper(ts, TokenStream::None);
+ return matchOrInsertSemicolonHelper(TokenStream::None);
}
-static bool
-MatchOrInsertSemicolonAfterNonExpression(TokenStream& ts)
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::matchOrInsertSemicolonAfterNonExpression()
{
- return MatchOrInsertSemicolonHelper(ts, TokenStream::Operand);
+ return matchOrInsertSemicolonHelper(TokenStream::Operand);
}
template <typename ParseHandler>
@@ -3020,7 +3043,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS
return false;
if (kind == Statement && fun->isExprBody()) {
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return false;
}
@@ -3503,7 +3526,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
if (tokenStream.hadError())
return false;
funbox->bufEnd = pos().end;
- if (kind == Statement && !MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
return false;
}
@@ -4639,7 +4662,7 @@ Parser<ParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isCon
* See 8.1.1.1.6 and the note in 13.2.1.
*/
Node decl = declarationList(yieldHandling, isConst ? PNK_CONST : PNK_LET);
- if (!decl || !MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!decl || !matchOrInsertSemicolonAfterExpression())
return null();
return decl;
@@ -4880,7 +4903,7 @@ Parser<FullParseHandler>::importDeclaration()
if (!moduleSpec)
return null();
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
ParseNode* node =
@@ -5023,7 +5046,7 @@ Parser<FullParseHandler>::exportDeclaration()
// export { x } // ExportDeclaration, terminated by ASI
// fro\u006D // ExpressionStatement, the name "from"
//
- // In that case let MatchOrInsertSemicolonAfterNonExpression sort out
+ // In that case let matchOrInsertSemicolonAfterNonExpression sort out
// ASI or any necessary error.
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -5039,7 +5062,7 @@ Parser<FullParseHandler>::exportDeclaration()
if (!moduleSpec)
return null();
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
@@ -5051,7 +5074,7 @@ Parser<FullParseHandler>::exportDeclaration()
tokenStream.ungetToken();
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
break;
}
@@ -5085,7 +5108,7 @@ Parser<FullParseHandler>::exportDeclaration()
if (!moduleSpec)
return null();
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
@@ -5121,7 +5144,7 @@ Parser<FullParseHandler>::exportDeclaration()
kid = declarationList(YieldIsName, PNK_VAR);
if (!kid)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
if (!checkExportedNamesForDeclaration(kid))
return null();
@@ -5174,7 +5197,7 @@ Parser<FullParseHandler>::exportDeclaration()
kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
if (!kid)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
break;
}
@@ -5239,7 +5262,7 @@ Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPr
/* possibleError = */ nullptr, invoked);
if (!pnexpr)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
return handler.newExprStatement(pnexpr, pos().end);
}
@@ -5870,7 +5893,7 @@ Parser<ParseHandler>::continueStatement(YieldHandling yieldHandling)
return null();
}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
return handler.newContinueStatement(label, TokenPos(begin, pos().end));
@@ -5910,7 +5933,7 @@ Parser<ParseHandler>::breakStatement(YieldHandling yieldHandling)
}
}
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
return handler.newBreakStatement(label, TokenPos(begin, pos().end));
@@ -5950,10 +5973,10 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
}
if (exprNode) {
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
} else {
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
}
@@ -6250,7 +6273,7 @@ Parser<ParseHandler>::throwStatement(YieldHandling yieldHandling)
if (!throwExpr)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
@@ -6477,7 +6500,7 @@ Parser<ParseHandler>::debuggerStatement()
{
TokenPos p;
p.begin = pos().begin;
- if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterNonExpression())
return null();
p.end = pos().end;
@@ -6784,7 +6807,7 @@ Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
Node vars = declarationList(yieldHandling, PNK_VAR);
if (!vars)
return null();
- if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
+ if (!matchOrInsertSemicolonAfterExpression())
return null();
return vars;
}
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index 156a1c1b0..41abb6d76 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1354,6 +1354,10 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter
bool finishFunction();
bool leaveInnerFunction(ParseContext* outerpc);
+ bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier);
+ bool matchOrInsertSemicolonAfterExpression();
+ bool matchOrInsertSemicolonAfterNonExpression();
+
public:
enum FunctionCallBehavior {
PermitAssignmentToFunctionCalls,
diff --git a/js/src/js.msg b/js/src/js.msg
index 4ded69a25..50817f50f 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -186,6 +186,7 @@ MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as'
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async")
MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression")
+MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
diff --git a/js/src/tests/ecma_2017/AsyncFunctions/await-error.js b/js/src/tests/ecma_2017/AsyncFunctions/await-error.js
new file mode 100644
index 000000000..1f40ea8a0
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/await-error.js
@@ -0,0 +1,16 @@
+var BUGNUMBER = 1317153;
+var summary = "await outside of async function should provide better error";
+
+print(BUGNUMBER + ": " + summary);
+
+let caught = false;
+try {
+ eval("await 10");
+} catch(e) {
+ assertEq(e.message, "await is only valid in async functions");
+ caught = true;
+}
+assertEq(caught, true);
+
+if (typeof reportCompare === "function")
+ reportCompare(true, true);