diff options
author | Gaming4JC <g4jc@hyperbola.info> | 2019-06-08 13:34:41 -0400 |
---|---|---|
committer | Gaming4JC <g4jc@hyperbola.info> | 2019-07-18 22:38:14 -0400 |
commit | f319f49a5280a7ba37cb551a7236505e496ae34a (patch) | |
tree | 6992d5f1e1522f1953169b1b84f193c081e2d8e9 /js/src | |
parent | dcb43bda93f1f2cbe3da5ba1b7d3f783f1c8ad1c (diff) | |
download | UXP-f319f49a5280a7ba37cb551a7236505e496ae34a.tar UXP-f319f49a5280a7ba37cb551a7236505e496ae34a.tar.gz UXP-f319f49a5280a7ba37cb551a7236505e496ae34a.tar.lz UXP-f319f49a5280a7ba37cb551a7236505e496ae34a.tar.xz UXP-f319f49a5280a7ba37cb551a7236505e496ae34a.zip |
1317153 - Provide better error message when errornous syntax possibly match "await SOMETHING" outside async function.
Diffstat (limited to 'js/src')
-rw-r--r-- | js/src/frontend/Parser.cpp | 89 | ||||
-rw-r--r-- | js/src/frontend/Parser.h | 4 | ||||
-rw-r--r-- | js/src/js.msg | 1 | ||||
-rw-r--r-- | js/src/tests/ecma_2017/AsyncFunctions/await-error.js | 16 |
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); |