From 5e76f72c539cd0e2fc0d38e4475f494868b5e859 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 13:02:16 -0400 Subject: 1315815 - Don't treat async or await as a keyword when they contain escapes. --- js/src/frontend/FullParseHandler.h | 20 ++++---- js/src/frontend/Parser.cpp | 95 +++++++++++++++++++++--------------- js/src/frontend/SyntaxParseHandler.h | 29 +++++------ 3 files changed, 80 insertions(+), 64 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index b619cf24c..3938d9743 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -10,6 +10,8 @@ #include "mozilla/Attributes.h" #include "mozilla/PodOperations.h" +#include + #include "frontend/ParseNode.h" #include "frontend/SharedContext.h" @@ -870,29 +872,25 @@ class FullParseHandler return node->isKind(PNK_NAME); } - bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); - - return node->pn_atom == cx->names().eval; + bool isEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval; } const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { MOZ_ASSERT(isNameAnyParentheses(node), "must only call this function on known names"); - if (nameIsEvalAnyParentheses(node, cx)) + if (isEvalAnyParentheses(node, cx)) return js_eval_str; if (node->pn_atom == cx->names().arguments) return js_arguments_str; return nullptr; } - bool nameIsUnparenthesizedAsync(ParseNode* node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); - - return node->pn_atom == cx->names().async; + bool isAsyncKeyword(ParseNode* node, ExclusiveContext* cx) { + return node->isKind(PNK_NAME) && + node->pn_pos.begin + strlen("async") == node->pn_pos.end && + node->pn_atom == cx->names().async; } bool isCall(ParseNode* pn) { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 3b7a0e612..7d923420a 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -5147,7 +5147,10 @@ Parser::exportDeclaration() return null(); break; default: { - if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) { + if (tt == TOK_NAME && + tokenStream.currentName() == context->names().async && + !tokenStream.currentToken().nameContainsEscape()) + { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); @@ -7045,21 +7048,23 @@ Parser::statementListItem(YieldHandling yieldHandling, if (!tokenStream.peekToken(&next)) return null(); - if (!tokenStream.currentToken().nameContainsEscape() && - tokenStream.currentName() == context->names().let && - nextTokenContinuesLetDeclaration(next, yieldHandling)) - { - return lexicalDeclaration(yieldHandling, /* isConst = */ false); - } + if (!tokenStream.currentToken().nameContainsEscape()) { + if (tokenStream.currentName() == context->names().let && + nextTokenContinuesLetDeclaration(next, yieldHandling)) + { + return lexicalDeclaration(yieldHandling, /* isConst = */ false); + } - if (tokenStream.currentName() == context->names().async) { - TokenKind nextSameLine = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&nextSameLine)) - return null(); - if (nextSameLine == TOK_FUNCTION) { - uint32_t preludeStart = pos().begin; - tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction); + if (tokenStream.currentName() == context->names().async) { + TokenKind nextSameLine = TOK_EOF; + if (!tokenStream.peekTokenSameLine(&nextSameLine)) { + return null(); + } + if (nextSameLine == TOK_FUNCTION) { + uint32_t preludeStart = pos().begin; + tokenStream.consumeKnownToken(TOK_FUNCTION); + return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction); + } } } @@ -7516,7 +7521,10 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl return yieldExpression(inHandling); bool maybeAsyncArrow = false; - if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) { + if (tt == TOK_NAME && + tokenStream.currentName() == context->names().async && + !tokenStream.currentToken().nameContainsEscape()) + { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); @@ -7537,6 +7545,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (maybeAsyncArrow) { tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand); MOZ_ASSERT(tokenStream.currentName() == context->names().async); + MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape()); TokenKind tt; if (!tokenStream.getToken(&tt)) @@ -7613,7 +7622,9 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (next == TOK_NAME) { tokenStream.consumeKnownToken(next, TokenStream::Operand); - if (tokenStream.currentName() == context->names().async) { + if (tokenStream.currentName() == context->names().async && + !tokenStream.currentToken().nameContainsEscape()) + { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); @@ -8448,8 +8459,27 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling JSOp op = JSOP_CALL; bool maybeAsyncArrow = false; - if (tt == TOK_LP && handler.isNameAnyParentheses(lhs)) { - if (handler.nameIsEvalAnyParentheses(lhs, context)) { + if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { + // Use the JSOP_FUN{APPLY,CALL} optimizations given the + // right syntax. + if (prop == context->names().apply) { + op = JSOP_FUNAPPLY; + if (pc->isFunctionBox()) { + pc->functionBox()->usesApply = true; + } + } else if (prop == context->names().call) { + op = JSOP_FUNCALL; + } + } else if (tt == TOK_LP) { + if (handler.isAsyncKeyword(lhs, context)) { + // |async (| can be the start of an async arrow + // function, so we need to defer reporting possible + // errors from destructuring syntax. To give better + // error messages, we only allow the AsyncArrowHead + // part of the CoverCallExpressionAndAsyncArrowHead + // syntax when the initial name is "async". + maybeAsyncArrow = true; + } else if (handler.isEvalAnyParentheses(lhs, context)) { // Select the right EVAL op and flag pc as having a // direct eval. op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; @@ -8466,24 +8496,6 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling // it. (If we're not in a method, that's fine, so // ignore the return value.) checkAndMarkSuperScope(); - } else if (handler.nameIsUnparenthesizedAsync(lhs, context)) { - // |async (| can be the start of an async arrow - // function, so we need to defer reporting possible - // errors from destructuring syntax. To give better - // error messages, we only allow the AsyncArrowHead - // part of the CoverCallExpressionAndAsyncArrowHead - // syntax when the initial name is "async". - maybeAsyncArrow = true; - } - } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) { - // Use the JSOP_FUN{APPLY,CALL} optimizations given the - // right syntax. - if (prop == context->names().apply) { - op = JSOP_FUNAPPLY; - if (pc->isFunctionBox()) - pc->functionBox()->usesApply = true; - } else if (prop == context->names().call) { - op = JSOP_FUNCALL; } } @@ -8816,7 +8828,10 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return null(); } - if (ltok == TOK_NAME && tokenStream.currentName() == context->names().async) { + if (ltok == TOK_NAME && + tokenStream.currentName() == context->names().async && + !tokenStream.currentToken().nameContainsEscape()) + { // AsyncMethod[Yield, Await]: // async [no LineTerminator here] PropertyName[?Yield, ?Await] ... // @@ -9404,7 +9419,9 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling case TOK_YIELD: case TOK_NAME: { - if (tokenStream.currentName() == context->names().async) { + if (tokenStream.currentName() == context->names().async && + !tokenStream.currentToken().nameContainsEscape()) + { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 00ea9d35d..9dc3c1072 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -9,6 +9,8 @@ #include "mozilla/Attributes.h" +#include + #include "frontend/ParseNode.h" #include "frontend/TokenStream.h" @@ -94,10 +96,13 @@ class SyntaxParseHandler // Nodes representing unparenthesized names. NodeUnparenthesizedArgumentsName, - NodeUnparenthesizedAsyncName, NodeUnparenthesizedEvalName, NodeUnparenthesizedName, + // Node representing the "async" name, which may actually be a + // contextual keyword. + NodePotentialAsyncKeyword, + // Valuable for recognizing potential destructuring patterns. NodeUnparenthesizedArray, NodeUnparenthesizedObject, @@ -183,8 +188,8 @@ class SyntaxParseHandler lastAtom = name; if (name == cx->names().arguments) return NodeUnparenthesizedArgumentsName; - if (name == cx->names().async) - return NodeUnparenthesizedAsyncName; + if (pos.begin + strlen("async") == pos.end && name == cx->names().async) + return NodePotentialAsyncKeyword; if (name == cx->names().eval) return NodeUnparenthesizedEvalName; return NodeUnparenthesizedName; @@ -497,7 +502,7 @@ class SyntaxParseHandler return NodeParenthesizedArgumentsName; if (node == NodeUnparenthesizedEvalName) return NodeParenthesizedEvalName; - if (node == NodeUnparenthesizedName || node == NodeUnparenthesizedAsyncName) + if (node == NodeUnparenthesizedName || node == NodePotentialAsyncKeyword) return NodeParenthesizedName; if (node == NodeUnparenthesizedArray) @@ -528,9 +533,9 @@ class SyntaxParseHandler bool isUnparenthesizedName(Node node) { return node == NodeUnparenthesizedArgumentsName || - node == NodeUnparenthesizedAsyncName || node == NodeUnparenthesizedEvalName || - node == NodeUnparenthesizedName; + node == NodeUnparenthesizedName || + node == NodePotentialAsyncKeyword; } bool isNameAnyParentheses(Node node) { @@ -541,9 +546,7 @@ class SyntaxParseHandler node == NodeParenthesizedName; } - bool nameIsEvalAnyParentheses(Node node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); + bool isEvalAnyParentheses(Node node, ExclusiveContext* cx) { return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName; } @@ -551,17 +554,15 @@ class SyntaxParseHandler MOZ_ASSERT(isNameAnyParentheses(node), "must only call this method on known names"); - if (nameIsEvalAnyParentheses(node, cx)) + if (isEvalAnyParentheses(node, cx)) return js_eval_str; if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName) return js_arguments_str; return nullptr; } - bool nameIsUnparenthesizedAsync(Node node, ExclusiveContext* cx) { - MOZ_ASSERT(isNameAnyParentheses(node), - "must only call this function on known names"); - return node == NodeUnparenthesizedAsyncName; + bool isAsyncKeyword(Node node, ExclusiveContext* cx) { + return node == NodePotentialAsyncKeyword; } PropertyName* maybeDottedProperty(Node node) { -- cgit v1.2.3 From f319f49a5280a7ba37cb551a7236505e496ae34a Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 13:34:41 -0400 Subject: 1317153 - Provide better error message when errornous syntax possibly match "await SOMETHING" outside async function. --- js/src/frontend/Parser.cpp | 89 +++++++++++++++++++++++++++++----------------- js/src/frontend/Parser.h | 4 +++ 2 files changed, 60 insertions(+), 33 deletions(-) (limited to 'js/src/frontend') 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::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 +bool +Parser::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 +bool +Parser::matchOrInsertSemicolonAfterExpression() { - return MatchOrInsertSemicolonHelper(ts, TokenStream::None); + return matchOrInsertSemicolonHelper(TokenStream::None); } -static bool -MatchOrInsertSemicolonAfterNonExpression(TokenStream& ts) +template +bool +Parser::matchOrInsertSemicolonAfterNonExpression() { - return MatchOrInsertSemicolonHelper(ts, TokenStream::Operand); + return matchOrInsertSemicolonHelper(TokenStream::Operand); } template @@ -3020,7 +3043,7 @@ Parser::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::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::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::importDeclaration() if (!moduleSpec) return null(); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); ParseNode* node = @@ -5023,7 +5046,7 @@ Parser::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::exportDeclaration() if (!moduleSpec) return null(); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec); @@ -5051,7 +5074,7 @@ Parser::exportDeclaration() tokenStream.ungetToken(); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); break; } @@ -5085,7 +5108,7 @@ Parser::exportDeclaration() if (!moduleSpec) return null(); - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec); @@ -5121,7 +5144,7 @@ Parser::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::exportDeclaration() kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); if (!kid) return null(); - if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterExpression()) return null(); break; } @@ -5239,7 +5262,7 @@ Parser::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::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::breakStatement(YieldHandling yieldHandling) } } - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); return handler.newBreakStatement(label, TokenPos(begin, pos().end)); @@ -5950,10 +5973,10 @@ Parser::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::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::debuggerStatement() { TokenPos p; p.begin = pos().begin; - if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream)) + if (!matchOrInsertSemicolonAfterNonExpression()) return null(); p.end = pos().end; @@ -6784,7 +6807,7 @@ Parser::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, -- cgit v1.2.3 From 987d6726f3c64d1cc510acba1de08deb5e0a7702 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 14:34:29 -0400 Subject: 1296814 - Move FunctionDeclaration-as-consequent/alternative handling out of Parser::functionStmt into Parser::consequentOrAlternative. --- js/src/frontend/Parser.cpp | 56 ++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 32 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 3c509465f..8f52f1d27 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3550,21 +3550,6 @@ Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHan { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); - // Annex B.3.4 says we can parse function declarations unbraced under if - // or else as if it were braced. That is, |if (x) function f() {}| is - // parsed as |if (x) { function f() {} }|. - Maybe synthesizedStmtForAnnexB; - Maybe synthesizedScopeForAnnexB; - if (!pc->sc()->strict()) { - ParseContext::Statement* stmt = pc->innermostStatement(); - if (stmt && stmt->kind() == StatementKind::If) { - synthesizedStmtForAnnexB.emplace(pc, StatementKind::Block); - synthesizedScopeForAnnexB.emplace(this); - if (!synthesizedScopeForAnnexB->init(pc)) - return null(); - } - } - // In sloppy mode, Annex B.3.2 allows labelled function declarations. // Otherwise it's a parse error. ParseContext::Statement* declaredInStmt = pc->innermostStatement(); @@ -3645,20 +3630,8 @@ Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHan return null(); YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); - Node fun = functionDefinition(preludeStart, pn, InAllowed, newYieldHandling, + return functionDefinition(preludeStart, pn, InAllowed, newYieldHandling, name, Statement, generatorKind, asyncKind, tryAnnexB); - if (!fun) - return null(); - - if (synthesizedStmtForAnnexB) { - Node synthesizedStmtList = handler.newStatementList(handler.getPosition(fun)); - if (!synthesizedStmtList) - return null(); - handler.addStatementToList(synthesizedStmtList, fun); - return finishLexicalScope(*synthesizedScopeForAnnexB, synthesizedStmtList); - } - - return fun; } template @@ -5276,13 +5249,32 @@ Parser::consequentOrAlternative(YieldHandling yieldHandling) return null(); if (next == TOK_FUNCTION) { - // Apply Annex B.3.4 in non-strict code to allow FunctionDeclaration as - // the consequent/alternative of an |if| or |else|. Parser::statement - // will report the strict mode error. + // 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); - return functionStmt(pos().begin, yieldHandling, NameRequired); + + 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); } + + // Function declarations are a syntax error in strict mode code. + // Parser::statement reports that error. } return statement(yieldHandling); -- cgit v1.2.3 From 7ecc50d90d13690d610f26d0056a326e52bc834c Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 14:51:46 -0400 Subject: 1317379 - Disallow generator functions and async functions as direct children of if/else. --- js/src/frontend/Parser.cpp | 83 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 21 deletions(-) (limited to 'js/src/frontend') 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::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); -- cgit v1.2.3 From 986ae6266566447f22be68caf6371cbf98cafd52 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:03:53 -0400 Subject: 636635 - Do not create named lambda binding for a function created by Function constructor. --- js/src/frontend/Parser.cpp | 21 +++++++++++---------- js/src/frontend/Parser.h | 7 ++++--- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index a7b1f3a14..c86d9ca7b 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2173,7 +2173,7 @@ Parser::declareDotGeneratorName() template bool -Parser::finishFunctionScopes() +Parser::finishFunctionScopes(bool isStandaloneFunction) { FunctionBox* funbox = pc->functionBox(); @@ -2182,7 +2182,7 @@ Parser::finishFunctionScopes() return false; } - if (funbox->function()->isNamedLambda()) { + if (funbox->function()->isNamedLambda() && !isStandaloneFunction) { if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope())) return false; } @@ -2192,9 +2192,9 @@ Parser::finishFunctionScopes() template <> bool -Parser::finishFunction() +Parser::finishFunction(bool isStandaloneFunction /* = false */) { - if (!finishFunctionScopes()) + if (!finishFunctionScopes(isStandaloneFunction)) return false; FunctionBox* funbox = pc->functionBox(); @@ -2215,7 +2215,7 @@ Parser::finishFunction() funbox->functionScopeBindings().set(*bindings); } - if (funbox->function()->isNamedLambda()) { + if (funbox->function()->isNamedLambda() && !isStandaloneFunction) { Maybe bindings = newLexicalScopeData(pc->namedLambdaScope()); if (!bindings) return false; @@ -2227,14 +2227,14 @@ Parser::finishFunction() template <> bool -Parser::finishFunction() +Parser::finishFunction(bool isStandaloneFunction /* = false */) { // The LazyScript for a lazily parsed function needs to know its set of // free variables and inner functions so that when it is fully parsed, we // can skip over any already syntax parsed inner functions and still // retain correct scope information. - if (!finishFunctionScopes()) + if (!finishFunctionScopes(isStandaloneFunction)) return false; // There are too many bindings or inner functions to be saved into the @@ -2354,7 +2354,7 @@ Parser::standaloneFunction(HandleFunction fun, YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, - parameterListEnd)) + parameterListEnd, /* isStandaloneFunction = */ true)) { return null(); } @@ -3425,7 +3425,8 @@ bool Parser::functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling, Node pn, FunctionSyntaxKind kind, - Maybe parameterListEnd /* = Nothing() */) + Maybe parameterListEnd /* = Nothing() */, + bool isStandaloneFunction /* = false */) { // Given a properly initialized parse context, try to parse an actual // function without concern for conversion to strict mode, use of lazy @@ -3533,7 +3534,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject()) funbox->setNeedsHomeObject(); - if (!finishFunction()) + if (!finishFunction(isStandaloneFunction)) return false; handler.setEndPosition(body, pos().begin); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 41abb6d76..f6cff8a6c 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1070,7 +1070,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // ParseContext is already on the stack. bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling, Node pn, FunctionSyntaxKind kind, - mozilla::Maybe parameterListEnd = mozilla::Nothing()); + mozilla::Maybe parameterListEnd = mozilla::Nothing(), + bool isStandaloneFunction = false); // Determine whether |yield| is a valid name in the current context, or @@ -1350,8 +1351,8 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); - bool finishFunctionScopes(); - bool finishFunction(); + bool finishFunctionScopes(bool isStandaloneFunction); + bool finishFunction(bool isStandaloneFunction = false); bool leaveInnerFunction(ParseContext* outerpc); bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier); -- cgit v1.2.3 From 90d999c59a08bfc3145317aa4f0a92db0597632e Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:12:00 -0400 Subject: 1320403 - Move JSFunction::EXPR_BODY to JSScript, LazyScript, and FunctionBox. --- js/src/frontend/Parser.cpp | 18 +++++++++++++----- js/src/frontend/SharedContext.h | 8 ++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c86d9ca7b..2a80afbf1 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -474,6 +474,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac usesThis(false), usesReturn(false), hasRest_(false), + isExprBody_(false), funCxFlags() { // Functions created at parse time may be set singleton after parsing and @@ -2264,6 +2265,8 @@ Parser::finishFunction(bool isStandaloneFunction /* = false lazy->setAsyncKind(funbox->asyncKind()); if (funbox->hasRest()) lazy->setHasRest(); + if (funbox->isExprBody()) + lazy->setIsExprBody(); if (funbox->isLikelyConstructorWrapper()) lazy->setLikelyConstructorWrapper(); if (funbox->isDerivedClassConstructor()) @@ -3030,6 +3033,8 @@ Parser::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS LazyScript* lazy = fun->lazyScript(); if (lazy->needsHomeObject()) funbox->setNeedsHomeObject(); + if (lazy->isExprBody()) + funbox->setIsExprBody(); PropagateTransitiveParseFlags(lazy, pc->sc()); @@ -3042,10 +3047,15 @@ Parser::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase)) return false; - if (kind == Statement && fun->isExprBody()) { +#if JS_HAS_EXPR_CLOSURES + // Only expression closure can be Statement kind. + // If we remove expression closure, we can remove isExprBody flag from + // LazyScript and JSScript. + if (kind == Statement && funbox->isExprBody()) { if (!matchOrInsertSemicolonAfterExpression()) return false; } +#endif return true; } @@ -3490,9 +3500,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, tokenStream.ungetToken(); bodyType = ExpressionBody; -#if JS_HAS_EXPR_CLOSURES - fun->setIsExprBody(); -#endif + funbox->setIsExprBody(); } // Arrow function parameters inherit yieldHandling from the enclosing @@ -6130,7 +6138,7 @@ Parser::yieldExpression(InHandling inHandling) if (pc->funHasReturnExpr #if JS_HAS_EXPR_CLOSURES - || pc->functionBox()->function()->isExprBody() + || pc->functionBox()->isExprBody() #endif ) { diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index b20417d5d..f5a74e18c 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -473,6 +473,9 @@ class FunctionBox : public ObjectBox, public SharedContext bool usesThis:1; /* contains 'this' */ bool usesReturn:1; /* contains a 'return' statement */ bool hasRest_:1; /* has rest parameter */ + bool isExprBody_:1; /* arrow function with expression + * body or expression closure: + * function(x) x*x */ FunctionContextFlags funCxFlags; @@ -546,6 +549,11 @@ class FunctionBox : public ObjectBox, public SharedContext hasRest_ = true; } + bool isExprBody() const { return isExprBody_; } + void setIsExprBody() { + isExprBody_ = true; + } + void setGeneratorKind(GeneratorKind kind) { // A generator kind can be set at initialization, or when "yield" is // first seen. In both cases the transition can only happen from -- cgit v1.2.3 From c3e3a917d634ee1e550b2a19096ee99dc2024139 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:29:37 -0400 Subject: 1325157 - Implement a ParserBase class that holds functionality that's identical between syntax parsing and full parsing. --- js/src/frontend/Parser.cpp | 100 +++++++++--------- js/src/frontend/Parser.h | 251 +++++++++++++++++++++++---------------------- 2 files changed, 181 insertions(+), 170 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 2a80afbf1..79b5d70f4 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -571,9 +571,8 @@ FunctionBox::initWithEnclosingScope(Scope* enclosingScope) computeInWith(enclosingScope); } -template void -Parser::error(unsigned errorNumber, ...) +ParserBase::error(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -585,9 +584,8 @@ Parser::error(unsigned errorNumber, ...) va_end(args); } -template void -Parser::errorAt(uint32_t offset, unsigned errorNumber, ...) +ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -599,9 +597,8 @@ Parser::errorAt(uint32_t offset, unsigned errorNumber, ...) va_end(args); } -template bool -Parser::warning(unsigned errorNumber, ...) +ParserBase::warning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -611,9 +608,8 @@ Parser::warning(unsigned errorNumber, ...) return result; } -template bool -Parser::warningAt(uint32_t offset, unsigned errorNumber, ...) +ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -623,9 +619,8 @@ Parser::warningAt(uint32_t offset, unsigned errorNumber, ...) return result; } -template bool -Parser::extraWarning(unsigned errorNumber, ...) +ParserBase::extraWarning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -634,9 +629,8 @@ Parser::extraWarning(unsigned errorNumber, ...) return result; } -template bool -Parser::strictModeError(unsigned errorNumber, ...) +ParserBase::strictModeError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -647,9 +641,8 @@ Parser::strictModeError(unsigned errorNumber, ...) return res; } -template bool -Parser::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) +ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -659,9 +652,8 @@ Parser::strictModeErrorAt(uint32_t offset, unsigned errorNumber, . return res; } -template bool -Parser::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) +ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -702,16 +694,14 @@ Parser::abortIfSyntaxParser() return false; } -template -Parser::Parser(ExclusiveContext* cx, LifoAlloc& alloc, - const ReadOnlyCompileOptions& options, - const char16_t* chars, size_t length, - bool foldConstants, - UsedNameTracker& usedNames, - Parser* syntaxParser, - LazyScript* lazyOuterFunction) - : AutoGCRooter(cx, PARSER), - context(cx), +ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, + const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, + bool foldConstants, + UsedNameTracker& usedNames, + Parser* syntaxParser, + LazyScript* lazyOuterFunction) + : context(cx), alloc(alloc), tokenStream(cx, options, chars, length, thisForCtor()), traceListHead(nullptr), @@ -725,18 +715,44 @@ Parser::Parser(ExclusiveContext* cx, LifoAlloc& alloc, checkOptionsCalled(false), #endif abortedSyntaxParse(false), - isUnexpectedEOF_(false), - handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction) + isUnexpectedEOF_(false) { cx->perThreadData->frontendCollectionPool.addActiveCompilation(); + tempPoolMark = alloc.mark(); +} + +ParserBase::~ParserBase() +{ + alloc.release(tempPoolMark); + + /* + * The parser can allocate enormous amounts of memory for large functions. + * Eagerly free the memory now (which otherwise won't be freed until the + * next GC) to avoid unnecessary OOMs. + */ + alloc.freeAllIfHugeAndUnused(); + + context->perThreadData->frontendCollectionPool.removeActiveCompilation(); +} +template +Parser::Parser(ExclusiveContext* cx, LifoAlloc& alloc, + const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, + bool foldConstants, + UsedNameTracker& usedNames, + Parser* syntaxParser, + LazyScript* lazyOuterFunction) + : ParserBase(cx, alloc, options, chars, length, foldConstants, usedNames, syntaxParser, + lazyOuterFunction), + AutoGCRooter(cx, PARSER), + handler(cx, alloc, tokenStream, syntaxParser, lazyOuterFunction) +{ // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings // which are not generated if functions are parsed lazily. Note that the // standard "use strict" does not inhibit lazy parsing. if (options.extraWarningsOption) handler.disableSyntaxParser(); - - tempPoolMark = alloc.mark(); } template @@ -747,26 +763,13 @@ Parser::checkOptions() checkOptionsCalled = true; #endif - if (!tokenStream.checkOptions()) - return false; - - return true; + return tokenStream.checkOptions(); } template Parser::~Parser() { MOZ_ASSERT(checkOptionsCalled); - alloc.release(tempPoolMark); - - /* - * The parser can allocate enormous amounts of memory for large functions. - * Eagerly free the memory now (which otherwise won't be freed until the - * next GC) to avoid unnecessary OOMs. - */ - alloc.freeAllIfHugeAndUnused(); - - context->perThreadData->frontendCollectionPool.removeActiveCompilation(); } template @@ -897,9 +900,8 @@ Parser::parse() * Strict mode forbids introducing new definitions for 'eval', 'arguments', or * for any strict mode reserved keyword. */ -template bool -Parser::isValidStrictBinding(PropertyName* name) +ParserBase::isValidStrictBinding(PropertyName* name) { return name != context->names().eval && name != context->names().arguments && @@ -9602,9 +9604,8 @@ Parser::exprInParens(InHandling inHandling, YieldHandling yieldHan return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked); } -template bool -Parser::warnOnceAboutExprClosure() +ParserBase::warnOnceAboutExprClosure() { #ifndef RELEASE_OR_BETA JSContext* cx = context->maybeJSContext(); @@ -9620,9 +9621,8 @@ Parser::warnOnceAboutExprClosure() return true; } -template bool -Parser::warnOnceAboutForEach() +ParserBase::warnOnceAboutForEach() { JSContext* cx = context->maybeJSContext(); if (!cx) diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index f6cff8a6c..e36a6e8b2 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -734,8 +734,138 @@ class UsedNameTracker } }; +class ParserBase : public StrictModeGetter +{ + private: + ParserBase* thisForCtor() { return this; } + + public: + ExclusiveContext* const context; + + LifoAlloc& alloc; + + TokenStream tokenStream; + LifoAlloc::Mark tempPoolMark; + + /* list of parsed objects for GC tracing */ + ObjectBox* traceListHead; + + /* innermost parse context (stack-allocated) */ + ParseContext* pc; + + // For tracking used names in this parsing session. + UsedNameTracker& usedNames; + + /* Compression token for aborting. */ + SourceCompressionTask* sct; + + ScriptSource* ss; + + /* Root atoms and objects allocated for the parsed tree. */ + AutoKeepAtoms keepAtoms; + + /* Perform constant-folding; must be true when interfacing with the emitter. */ + const bool foldConstants:1; + + protected: +#if DEBUG + /* Our fallible 'checkOptions' member function has been called. */ + bool checkOptionsCalled:1; +#endif + + /* + * Not all language constructs can be handled during syntax parsing. If it + * is not known whether the parse succeeds or fails, this bit is set and + * the parse will return false. + */ + bool abortedSyntaxParse:1; + + /* Unexpected end of input, i.e. TOK_EOF not at top-level. */ + bool isUnexpectedEOF_:1; + + public: + ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, + const char16_t* chars, size_t length, bool foldConstants, + UsedNameTracker& usedNames, Parser* syntaxParser, + LazyScript* lazyOuterFunction); + ~ParserBase(); + + const char* getFilename() const { return tokenStream.getFilename(); } + JSVersion versionNumber() const { return tokenStream.versionNumber(); } + TokenPos pos() const { return tokenStream.currentToken().pos; } + + // Determine whether |yield| is a valid name in the current context, or + // whether it's prohibited due to strictness, JS version, or occurrence + // inside a star generator. + bool yieldExpressionsSupported() { + return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync(); + } + + virtual bool strictMode() { return pc->sc()->strict(); } + bool setLocalStrictMode(bool strict) { + MOZ_ASSERT(tokenStream.debugHasNoLookahead()); + return pc->sc()->setLocalStrictMode(strict); + } + + const ReadOnlyCompileOptions& options() const { + return tokenStream.options(); + } + + bool hadAbortedSyntaxParse() { + return abortedSyntaxParse; + } + void clearAbortedSyntaxParse() { + abortedSyntaxParse = false; + } + + bool isUnexpectedEOF() const { return isUnexpectedEOF_; } + + bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...); + + /* Report the given error at the current offset. */ + void error(unsigned errorNumber, ...); + + /* Report the given error at the given offset. */ + void errorAt(uint32_t offset, unsigned errorNumber, ...); + + /* + * Handle a strict mode error at the current offset. Report an error if in + * strict mode code, or warn if not, using the given error number and + * arguments. + */ + MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...); + + /* + * Handle a strict mode error at the given offset. Report an error if in + * strict mode code, or warn if not, using the given error number and + * arguments. + */ + MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...); + + /* Report the given warning at the current offset. */ + MOZ_MUST_USE bool warning(unsigned errorNumber, ...); + + /* Report the given warning at the given offset. */ + MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...); + + /* + * If extra warnings are enabled, report the given warning at the current + * offset. + */ + MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...); + + bool isValidStrictBinding(PropertyName* name); + + bool warnOnceAboutExprClosure(); + bool warnOnceAboutForEach(); + + protected: + enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true }; + enum ForInitLocation { InForInit, NotInForInit }; +}; + template -class Parser final : private JS::AutoGCRooter, public StrictModeGetter +class Parser final : public ParserBase, private JS::AutoGCRooter { private: using Node = typename ParseHandler::Node; @@ -852,50 +982,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter void transferErrorsTo(PossibleError* other); }; - public: - ExclusiveContext* const context; - - LifoAlloc& alloc; - - TokenStream tokenStream; - LifoAlloc::Mark tempPoolMark; - - /* list of parsed objects for GC tracing */ - ObjectBox* traceListHead; - - /* innermost parse context (stack-allocated) */ - ParseContext* pc; - - // For tracking used names in this parsing session. - UsedNameTracker& usedNames; - - /* Compression token for aborting. */ - SourceCompressionTask* sct; - - ScriptSource* ss; - - /* Root atoms and objects allocated for the parsed tree. */ - AutoKeepAtoms keepAtoms; - - /* Perform constant-folding; must be true when interfacing with the emitter. */ - const bool foldConstants:1; - - private: -#if DEBUG - /* Our fallible 'checkOptions' member function has been called. */ - bool checkOptionsCalled:1; -#endif - - /* - * Not all language constructs can be handled during syntax parsing. If it - * is not known whether the parse succeeds or fails, this bit is set and - * the parse will return false. - */ - bool abortedSyntaxParse:1; - - /* Unexpected end of input, i.e. TOK_EOF not at top-level. */ - bool isUnexpectedEOF_:1; - public: /* State specific to the kind of parse being performed. */ ParseHandler handler; @@ -904,40 +990,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter void freeTree(Node node) { handler.freeTree(node); } public: - bool reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumber, ...); - - /* Report the given error at the current offset. */ - void error(unsigned errorNumber, ...); - - /* Report the given error at the given offset. */ - void errorAt(uint32_t offset, unsigned errorNumber, ...); - - /* - * Handle a strict mode error at the current offset. Report an error if in - * strict mode code, or warn if not, using the given error number and - * arguments. - */ - MOZ_MUST_USE bool strictModeError(unsigned errorNumber, ...); - - /* - * Handle a strict mode error at the given offset. Report an error if in - * strict mode code, or warn if not, using the given error number and - * arguments. - */ - MOZ_MUST_USE bool strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...); - - /* Report the given warning at the current offset. */ - MOZ_MUST_USE bool warning(unsigned errorNumber, ...); - - /* Report the given warning at the given offset. */ - MOZ_MUST_USE bool warningAt(uint32_t offset, unsigned errorNumber, ...); - - /* - * If extra warnings are enabled, report the given warning at the current - * offset. - */ - MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...); - Parser(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames, Parser* syntaxParser, LazyScript* lazyOuterFunction); @@ -967,9 +1019,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter friend void js::frontend::MarkParser(JSTracer* trc, JS::AutoGCRooter* parser); - const char* getFilename() const { return tokenStream.getFilename(); } - JSVersion versionNumber() const { return tokenStream.versionNumber(); } - /* * Parse a top-level JS script. */ @@ -995,15 +1044,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter void trace(JSTracer* trc); - bool hadAbortedSyntaxParse() { - return abortedSyntaxParse; - } - void clearAbortedSyntaxParse() { - abortedSyntaxParse = false; - } - - bool isUnexpectedEOF() const { return isUnexpectedEOF_; } - bool checkUnescapedName(); private: @@ -1072,34 +1112,12 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter Node pn, FunctionSyntaxKind kind, mozilla::Maybe parameterListEnd = mozilla::Nothing(), bool isStandaloneFunction = false); - - - // Determine whether |yield| is a valid name in the current context, or - // whether it's prohibited due to strictness, JS version, or occurrence - // inside a star generator. - bool yieldExpressionsSupported() { - return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync(); - } // Match the current token against the BindingIdentifier production with // the given Yield parameter. If there is no match, report a syntax // error. PropertyName* bindingIdentifier(YieldHandling yieldHandling); - virtual bool strictMode() { return pc->sc()->strict(); } - bool setLocalStrictMode(bool strict) { - MOZ_ASSERT(tokenStream.debugHasNoLookahead()); - return pc->sc()->setLocalStrictMode(strict); - } - - const ReadOnlyCompileOptions& options() const { - return tokenStream.options(); - } - - private: - enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true }; - enum ForInitLocation { InForInit, NotInForInit }; - private: /* * JS parsers, from lowest to highest precedence. @@ -1375,8 +1393,6 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter bool hasValidSimpleStrictParameterNames(); - bool isValidStrictBinding(PropertyName* name); - void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos); bool notePositionalFormalParameter(Node fn, HandlePropertyName name, bool disallowDuplicateParams, bool* duplicatedParam); @@ -1432,12 +1448,7 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom); - TokenPos pos() const { return tokenStream.currentToken().pos; } - bool asmJS(Node list); - - bool warnOnceAboutExprClosure(); - bool warnOnceAboutForEach(); }; } /* namespace frontend */ -- cgit v1.2.3 From f6193fd0f4689643f47c6ac9aa5aac3b8cc10213 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:37:06 -0400 Subject: 1326454 - Introduce TokenStream::error that reports an error at the current offset. --- js/src/frontend/TokenStream.cpp | 37 +++++++++++++++++++++++++++---------- js/src/frontend/TokenStream.h | 3 +++ 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 8438ff7c5..9becaf55e 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -554,8 +554,10 @@ TokenStream::advance(size_t position) MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type)); lookahead = 0; - if (flags.hitOOM) - return reportError(JSMSG_OUT_OF_MEMORY); + if (flags.hitOOM) { + error(JSMSG_OUT_OF_MEMORY); + return false; + } return true; } @@ -806,6 +808,19 @@ TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...) va_end(args); } +void +TokenStream::error(unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + // We have encountered a '\': check for a Unicode escape sequence after it. // Return the length of the escape sequence and the character code point (by // value) if we found a Unicode escape sequence. Otherwise, return 0. In both @@ -1119,8 +1134,10 @@ TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp) return true; } - if (kw->tokentype == TOK_RESERVED) - return reportError(JSMSG_RESERVED_ID, kw->chars); + if (kw->tokentype == TOK_RESERVED) { + error(JSMSG_RESERVED_ID, kw->chars); + return false; + } if (kw->tokentype == TOK_STRICT_RESERVED) return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars); @@ -1897,7 +1914,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) while ((c = getCharIgnoreEOL()) != untilChar) { if (c == EOF) { ungetCharIgnoreEOL(c); - reportError(JSMSG_UNTERMINATED_STRING); + error(JSMSG_UNTERMINATED_STRING); return false; } @@ -1920,7 +1937,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) if (peekChar() == '{') { uint32_t code; if (!getBracedUnicode(&code)) { - reportError(JSMSG_MALFORMED_ESCAPE, "Unicode"); + error(JSMSG_MALFORMED_ESCAPE, "Unicode"); return false; } @@ -1945,7 +1962,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) c = (c << 4) + JS7_UNHEX(cp[3]); skipChars(4); } else { - reportError(JSMSG_MALFORMED_ESCAPE, "Unicode"); + error(JSMSG_MALFORMED_ESCAPE, "Unicode"); return false; } break; @@ -1958,7 +1975,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); skipChars(2); } else { - reportError(JSMSG_MALFORMED_ESCAPE, "hexadecimal"); + error(JSMSG_MALFORMED_ESCAPE, "hexadecimal"); return false; } break; @@ -1974,7 +1991,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) // Strict mode code allows only \0, then a non-digit. if (val != 0 || JS7_ISDEC(c)) { if (parsingTemplate) { - reportError(JSMSG_DEPRECATED_OCTAL); + error(JSMSG_DEPRECATED_OCTAL); return false; } if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL)) @@ -2003,7 +2020,7 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) } else if (TokenBuf::isRawEOLChar(c)) { if (!parsingTemplate) { ungetCharIgnoreEOL(c); - reportError(JSMSG_UNTERMINATED_STRING); + error(JSMSG_UNTERMINATED_STRING); return false; } if (c == '\r') { diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 6ba9fba5a..f08d317ba 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -366,6 +366,9 @@ class MOZ_STACK_CLASS TokenStream bool reportErrorNoOffset(unsigned errorNumber, ...); bool reportWarning(unsigned errorNumber, ...); + // Report the given error at the current offset. + void error(unsigned errorNumber, ...); + static const uint32_t NoOffset = UINT32_MAX; // General-purpose error reporters. You should avoid calling these -- cgit v1.2.3 From 068916b59a847bede7c6c788e803442c1833fe67 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:40:56 -0400 Subject: 1326454 - Introduce TokenStream::warning that warns at the current offset. --- js/src/frontend/TokenStream.cpp | 16 +++++++++------- js/src/frontend/TokenStream.h | 8 +++++--- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 9becaf55e..e8d622d38 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -777,7 +777,7 @@ TokenStream::reportErrorNoOffset(unsigned errorNumber, ...) } bool -TokenStream::reportWarning(unsigned errorNumber, ...) +TokenStream::warning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); @@ -958,9 +958,10 @@ TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated, int32_t c; if (peekChars(directiveLength, peeked) && CharsMatch(peeked, directive)) { - if (shouldWarnDeprecated && - !reportWarning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma)) - return false; + if (shouldWarnDeprecated) { + if (!warning(JSMSG_DEPRECATED_PRAGMA, errorMsgPragma)) + return false; + } skipChars(directiveLength); tokenbuf.clear(); @@ -1555,10 +1556,11 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) // grammar. We might not always be so permissive, so we warn // about it. if (c >= '8') { - if (!reportWarning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) { + if (!warning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09")) goto error; - } - goto decimal; // use the decimal scanner for the rest of the number + + // Use the decimal scanner for the rest of the number. + goto decimal; } c = getCharIgnoreEOL(); } diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index f08d317ba..18e9cb3ca 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -364,16 +364,18 @@ class MOZ_STACK_CLASS TokenStream // TokenStream-specific error reporters. bool reportError(unsigned errorNumber, ...); bool reportErrorNoOffset(unsigned errorNumber, ...); - bool reportWarning(unsigned errorNumber, ...); // Report the given error at the current offset. void error(unsigned errorNumber, ...); + // Warn at the current offset. + MOZ_MUST_USE bool warning(unsigned errorNumber, ...); + static const uint32_t NoOffset = UINT32_MAX; // General-purpose error reporters. You should avoid calling these - // directly, and instead use the more succinct alternatives (e.g. - // reportError()) in TokenStream, Parser, and BytecodeEmitter. + // directly, and instead use the more succinct alternatives (error(), + // warning(), &c.) in TokenStream, Parser, and BytecodeEmitter. bool reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber, va_list args); bool reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber, -- cgit v1.2.3 From b41de8683d8d28d08a6e2b79511610a268342a0e Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:48:25 -0400 Subject: 1326454 - Rename TokenStream::getBracedUnicode to TokenStream::matchBracedUnicode and make its signature fallible. --- js/src/frontend/TokenStream.cpp | 57 ++++++++++++++++++++++++++++++----------- js/src/frontend/TokenStream.h | 5 +++- 2 files changed, 46 insertions(+), 16 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index e8d622d38..abb7228eb 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -821,6 +821,19 @@ TokenStream::error(unsigned errorNumber, ...) va_end(args); } +void +TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + // We have encountered a '\': check for a Unicode escape sequence after it. // Return the length of the escape sequence and the character code point (by // value) if we found a Unicode escape sequence. Otherwise, return 0. In both @@ -1869,32 +1882,47 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) } bool -TokenStream::getBracedUnicode(uint32_t* cp) +TokenStream::matchBracedUnicode(bool* matched, uint32_t* cp) { + if (peekChar() != '{') { + *matched = false; + return true; + } + consumeKnownChar('{'); + uint32_t start = userbuf.offset(); + bool first = true; - int32_t c; uint32_t code = 0; - while (true) { - c = getCharIgnoreEOL(); - if (c == EOF) + do { + int32_t c = getCharIgnoreEOL(); + if (c == EOF) { + error(JSMSG_MALFORMED_ESCAPE, "Unicode"); return false; + } if (c == '}') { - if (first) + if (first) { + error(JSMSG_MALFORMED_ESCAPE, "Unicode"); return false; + } break; } - if (!JS7_ISHEX(c)) + if (!JS7_ISHEX(c)) { + error(JSMSG_MALFORMED_ESCAPE, "Unicode"); return false; + } code = (code << 4) | JS7_UNHEX(c); - if (code > unicode::NonBMPMax) + if (code > unicode::NonBMPMax) { + errorAt(start, JSMSG_UNICODE_OVERFLOW, "escape sequence"); return false; + } first = false; - } + } while (true); + *matched = true; *cp = code; return true; } @@ -1936,12 +1964,11 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) // Unicode character specification. case 'u': { - if (peekChar() == '{') { - uint32_t code; - if (!getBracedUnicode(&code)) { - error(JSMSG_MALFORMED_ESCAPE, "Unicode"); - return false; - } + bool matched; + uint32_t code; + if (!matchBracedUnicode(&matched, &code)) + return false; + if (matched) { MOZ_ASSERT(code <= unicode::NonBMPMax); if (code < unicode::NonBMPMin) { diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 18e9cb3ca..19385b499 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -368,6 +368,9 @@ class MOZ_STACK_CLASS TokenStream // Report the given error at the current offset. void error(unsigned errorNumber, ...); + // Report the given error at the given offset. + void errorAt(uint32_t offset, unsigned errorNumber, ...); + // Warn at the current offset. MOZ_MUST_USE bool warning(unsigned errorNumber, ...); @@ -952,7 +955,7 @@ class MOZ_STACK_CLASS TokenStream MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier); - MOZ_MUST_USE bool getBracedUnicode(uint32_t* code); + MOZ_MUST_USE bool matchBracedUnicode(bool* matched, uint32_t* code); MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp); int32_t getChar(); -- cgit v1.2.3 From d400e9491c0be93f34f368227d6a3e4d056d9061 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:52:17 -0400 Subject: 1326454 - Make TokenStream::peekChar's signature fallible. --- js/src/frontend/TokenStream.cpp | 55 ++++++++++++++++++++++++++++++----------- js/src/frontend/TokenStream.h | 8 +++--- 2 files changed, 45 insertions(+), 18 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index abb7228eb..6c5ea8fe9 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -968,7 +968,6 @@ TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated, { MOZ_ASSERT(directiveLength <= 18); char16_t peeked[18]; - int32_t c; if (peekChars(directiveLength, peeked) && CharsMatch(peeked, directive)) { if (shouldWarnDeprecated) { @@ -979,18 +978,33 @@ TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated, skipChars(directiveLength); tokenbuf.clear(); - while ((c = peekChar()) && c != EOF && !unicode::IsSpaceOrBOM2(c)) { - getChar(); + do { + int32_t c; + if (!peekChar(&c)) + return false; + + if (c == EOF || unicode::IsSpaceOrBOM2(c)) + break; + + consumeKnownChar(c); + // Debugging directives can occur in both single- and multi-line // comments. If we're currently inside a multi-line comment, we also // need to recognize multi-line comment terminators. - if (isMultiline && c == '*' && peekChar() == '/') { - ungetChar('*'); - break; + if (isMultiline && c == '*') { + int32_t c2; + if (!peekChar(&c2)) + return false; + + if (c2 == '/') { + ungetChar('*'); + break; + } } + if (!tokenbuf.append(c)) return false; - } + } while (true); if (tokenbuf.empty()) { // The directive's URL was missing, but this is not quite an @@ -1722,7 +1736,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) case '/': // Look for a single-line comment. if (matchChar('/')) { - c = peekChar(); + if (!peekChar(&c)) + goto error; if (c == '@' || c == '#') { bool shouldWarn = getChar() == '@'; if (!getDirectives(false, shouldWarn)) @@ -1789,7 +1804,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) RegExpFlag reflags = NoFlags; unsigned length = tokenbuf.length() + 1; while (true) { - c = peekChar(); + if (!peekChar(&c)) + goto error; if (c == 'g' && !(reflags & GlobalFlag)) reflags = RegExpFlag(reflags | GlobalFlag); else if (c == 'i' && !(reflags & IgnoreCaseFlag)) @@ -1806,7 +1822,8 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) length++; } - c = peekChar(); + if (!peekChar(&c)) + goto error; if (JS7_ISLET(c)) { char buf[2] = { '\0', '\0' }; tp->pos.begin += length + 1; @@ -1829,8 +1846,13 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) case '-': if (matchChar('-')) { - if (peekChar() == '>' && !flags.isDirtyLine) + int32_t c2; + if (!peekChar(&c2)) + goto error; + + if (c2 == '>' && !flags.isDirtyLine) goto skipline; + tp->type = TOK_DEC; } else { tp->type = matchChar('=') ? TOK_SUBASSIGN : TOK_SUB; @@ -1884,7 +1906,10 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) bool TokenStream::matchBracedUnicode(bool* matched, uint32_t* cp) { - if (peekChar() != '{') { + int32_t c; + if (!peekChar(&c)) + return false; + if (c != '{') { *matched = false; return true; } @@ -2015,7 +2040,8 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) if (JS7_ISOCT(c)) { int32_t val = JS7_UNOCT(c); - c = peekChar(); + if (!peekChar(&c)) + return false; // Strict mode code allows only \0, then a non-digit. if (val != 0 || JS7_ISDEC(c)) { @@ -2031,7 +2057,8 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) if (JS7_ISOCT(c)) { val = 8 * val + JS7_UNOCT(c); getChar(); - c = peekChar(); + if (!peekChar(&c)) + return false; if (JS7_ISOCT(c)) { int32_t save = val; val = 8 * val + JS7_UNOCT(c); diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 19385b499..e0e4b959e 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -990,10 +990,10 @@ class MOZ_STACK_CLASS TokenStream MOZ_ASSERT(c == expect); } - int32_t peekChar() { - int32_t c = getChar(); - ungetChar(c); - return c; + MOZ_MUST_USE bool peekChar(int32_t* c) { + *c = getChar(); + ungetChar(*c); + return true; } void skipChars(int n) { -- cgit v1.2.3 From 0ed4cf321f276d88f80d13daf2644f9bc51b0957 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:54:42 -0400 Subject: 1326454 - Make TokenStream::skipChars{,IgnoreEOL} accept an unsigned integral number of chars to skip. --- js/src/frontend/TokenStream.cpp | 14 +++++++++++--- js/src/frontend/TokenStream.h | 10 +++++----- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 6c5ea8fe9..619285090 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -8,6 +8,7 @@ #include "frontend/TokenStream.h" +#include "mozilla/ArrayUtils.h" #include "mozilla/IntegerTypeTraits.h" #include "mozilla/PodOperations.h" @@ -33,6 +34,7 @@ using namespace js; using namespace js::frontend; +using mozilla::ArrayLength; using mozilla::Maybe; using mozilla::PodAssign; using mozilla::PodCopy; @@ -962,7 +964,7 @@ TokenStream::getDirectives(bool isMultiline, bool shouldWarnDeprecated) bool TokenStream::getDirective(bool isMultiline, bool shouldWarnDeprecated, - const char* directive, int directiveLength, + const char* directive, uint8_t directiveLength, const char* errorMsgPragma, UniqueTwoByteChars* destination) { @@ -1036,7 +1038,10 @@ TokenStream::getDisplayURL(bool isMultiline, bool shouldWarnDeprecated) // developer would like to refer to the source as from the source's actual // URL. - return getDirective(isMultiline, shouldWarnDeprecated, " sourceURL=", 11, + static const char sourceURLDirective[] = " sourceURL="; + constexpr uint8_t sourceURLDirectiveLength = ArrayLength(sourceURLDirective) - 1; + return getDirective(isMultiline, shouldWarnDeprecated, + sourceURLDirective, sourceURLDirectiveLength, "sourceURL", &displayURL_); } @@ -1046,7 +1051,10 @@ TokenStream::getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated) // Match comments of the form "//# sourceMappingURL=" or // "/\* //# sourceMappingURL= *\/" - return getDirective(isMultiline, shouldWarnDeprecated, " sourceMappingURL=", 18, + static const char sourceMappingURLDirective[] = " sourceMappingURL="; + constexpr uint8_t sourceMappingURLDirectiveLength = ArrayLength(sourceMappingURLDirective) - 1; + return getDirective(isMultiline, shouldWarnDeprecated, + sourceMappingURLDirective, sourceMappingURLDirectiveLength, "sourceMappingURL", &sourceMapURL_); } diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index e0e4b959e..a058759c6 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -972,7 +972,7 @@ class MOZ_STACK_CLASS TokenStream MOZ_MUST_USE bool getDirectives(bool isMultiline, bool shouldWarnDeprecated); MOZ_MUST_USE bool getDirective(bool isMultiline, bool shouldWarnDeprecated, - const char* directive, int directiveLength, + const char* directive, uint8_t directiveLength, const char* errorMsgPragma, UniquePtr* destination); MOZ_MUST_USE bool getDisplayURL(bool isMultiline, bool shouldWarnDeprecated); @@ -996,13 +996,13 @@ class MOZ_STACK_CLASS TokenStream return true; } - void skipChars(int n) { - while (--n >= 0) + void skipChars(uint8_t n) { + while (n-- > 0) getChar(); } - void skipCharsIgnoreEOL(int n) { - while (--n >= 0) + void skipCharsIgnoreEOL(uint8_t n) { + while (n-- > 0) getCharIgnoreEOL(); } -- cgit v1.2.3 From aa2decd154dbbd17e0eac9c570841eb445c63a3f Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:55:51 -0400 Subject: 1326454 - Add assertions to TokenStream::skipChars{,IgnoreEOL} verifying EOF isn't yet hit and that newlines aren't skipped, if appropriate. --- js/src/frontend/TokenStream.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index a058759c6..4efc31446 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -997,13 +997,18 @@ class MOZ_STACK_CLASS TokenStream } void skipChars(uint8_t n) { - while (n-- > 0) - getChar(); + while (n-- > 0) { + MOZ_ASSERT(userbuf.hasRawChars()); + mozilla::DebugOnly c = getCharIgnoreEOL(); + MOZ_ASSERT(c != '\n'); + } } void skipCharsIgnoreEOL(uint8_t n) { - while (n-- > 0) + while (n-- > 0) { + MOZ_ASSERT(userbuf.hasRawChars()); getCharIgnoreEOL(); + } } void updateLineInfoForEOL(); -- cgit v1.2.3 From c6fe42095040b0be2ee85bc750bd905bfe396fbd Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 15:57:27 -0400 Subject: 1326454 - Don't report an error when SourceCoords::add fails, because it fails only when an underlying Vector::append fails, and that vector handles OOM reporting itself. --- js/src/frontend/TokenStream.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 619285090..186b7a8ab 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -225,8 +225,13 @@ TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset) // only if lineStartOffsets_.append succeeds, to keep sentinel. // Otherwise return false to tell TokenStream about OOM. uint32_t maxPtr = MAX_PTR; - if (!lineStartOffsets_.append(maxPtr)) + if (!lineStartOffsets_.append(maxPtr)) { + static_assert(mozilla::IsSame::value, + "this function's caller depends on it reporting an " + "error on failure, as TempAllocPolicy ensures"); return false; + } lineStartOffsets_[lineIndex] = lineStartOffset; } else { @@ -557,7 +562,6 @@ TokenStream::advance(size_t position) lookahead = 0; if (flags.hitOOM) { - error(JSMSG_OUT_OF_MEMORY); return false; } @@ -1876,8 +1880,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) MOZ_CRASH("should have jumped to |out| or |error|"); out: - if (flags.hitOOM) - return reportError(JSMSG_OUT_OF_MEMORY); + if (flags.hitOOM) { + return false; + } flags.isDirtyLine = true; tp->pos.end = userbuf.offset(); @@ -1893,8 +1898,9 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) return true; error: - if (flags.hitOOM) - return reportError(JSMSG_OUT_OF_MEMORY); + if (flags.hitOOM) { + return false; + } flags.isDirtyLine = true; tp->pos.end = userbuf.offset(); -- cgit v1.2.3 From adc81d634d12d58a64e0d2a1e95998b766cf6e96 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 16:37:10 -0400 Subject: 1317374 - Don't do Annex B lexical function behavior when redeclaring a parameter name. --- js/src/frontend/Parser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 79b5d70f4..66ada79f2 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1183,6 +1183,12 @@ Parser::tryDeclareVar(HandlePropertyName name, DeclarationKind kin *redeclaredKind = Some(declaredKind); return true; } + } else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) { + MOZ_ASSERT(DeclarationKindIsParameter(declaredKind)); + + // Annex B.3.3.1 disallows redeclaring parameter names. + *redeclaredKind = Some(declaredKind); + return true; } } else { if (!scope->addDeclaredName(pc, p, name, kind)) -- cgit v1.2.3 From afb28a43d481075a244b0e18faa8447dfadacf8f Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 16:51:29 -0400 Subject: 1317375 - Implement "Template Literals Revision / Lifting Template Literal Restriction" ECMAScript proposal --- js/src/frontend/BytecodeEmitter.cpp | 5 ++ js/src/frontend/FoldConstants.cpp | 4 + js/src/frontend/FullParseHandler.h | 4 + js/src/frontend/NameFunctions.cpp | 6 +- js/src/frontend/ParseNode.cpp | 2 + js/src/frontend/ParseNode.h | 18 ++++- js/src/frontend/Parser.cpp | 25 +++++-- js/src/frontend/Parser.h | 3 +- js/src/frontend/SyntaxParseHandler.h | 1 + js/src/frontend/TokenStream.cpp | 140 +++++++++++++++++++++-------------- js/src/frontend/TokenStream.h | 62 +++++++++++++++- 11 files changed, 202 insertions(+), 68 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 7f9fa8a5d..44787dc52 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3069,6 +3069,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: @@ -6242,6 +6243,9 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje case PNK_NULL: vp.setNull(); return true; + case PNK_RAW_UNDEFINED: + vp.setUndefined(); + return true; case PNK_CALLSITEOBJ: case PNK_ARRAY: { unsigned count; @@ -10636,6 +10640,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: if (!emit1(pn->getOp())) return false; break; diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 6f62ffac6..441a72a45 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -378,6 +378,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_THIS: case PNK_ELISION: case PNK_NUMBER: @@ -468,6 +469,7 @@ IsEffectless(ParseNode* node) node->isKind(PNK_TEMPLATE_STRING) || node->isKind(PNK_NUMBER) || node->isKind(PNK_NULL) || + node->isKind(PNK_RAW_UNDEFINED) || node->isKind(PNK_FUNCTION) || node->isKind(PNK_GENEXP); } @@ -492,6 +494,7 @@ Boolish(ParseNode* pn) case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: return Falsy; case PNK_VOID: { @@ -1643,6 +1646,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_NUMBER: case PNK_DEBUGGER: diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 3938d9743..f0fc7700d 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -183,6 +183,10 @@ class FullParseHandler return new_(pos); } + ParseNode* newRawUndefinedLiteral(const TokenPos& pos) { + return new_(pos); + } + // The Boxer object here is any object that can allocate ObjectBoxes. // Specifically, a Boxer has a .newObjectBox(T) method that accepts a // Rooted argument and returns an ObjectBox*. diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index ce1318f0b..dc54d0a88 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -316,7 +316,8 @@ class NameResolver return false; // Next is the callsite object node. This node only contains - // internal strings and an array -- no user-controlled expressions. + // internal strings or undefined and an array -- no user-controlled + // expressions. element = element->pn_next; #ifdef DEBUG { @@ -326,7 +327,7 @@ class NameResolver for (ParseNode* kid = array->pn_head; kid; kid = kid->pn_next) MOZ_ASSERT(kid->isKind(PNK_TEMPLATE_STRING)); for (ParseNode* next = array->pn_next; next; next = next->pn_next) - MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING)); + MOZ_ASSERT(next->isKind(PNK_TEMPLATE_STRING) || next->isKind(PNK_RAW_UNDEFINED)); } #endif @@ -382,6 +383,7 @@ class NameResolver case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 91f17625c..5fe64e3d3 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -190,6 +190,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_TRUE: case PNK_FALSE: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_ELISION: case PNK_GENERATOR: case PNK_NUMBER: @@ -685,6 +686,7 @@ NullaryNode::dump() case PNK_TRUE: fprintf(stderr, "#true"); break; case PNK_FALSE: fprintf(stderr, "#false"); break; case PNK_NULL: fprintf(stderr, "#null"); break; + case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break; case PNK_NUMBER: { ToCStringBuf cbuf; diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index c3523afe4..c0669d85e 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -54,6 +54,7 @@ class ObjectBox; F(TRUE) \ F(FALSE) \ F(NULL) \ + F(RAW_UNDEFINED) \ F(THIS) \ F(FUNCTION) \ F(MODULE) \ @@ -406,7 +407,8 @@ IsTypeofKind(ParseNodeKind kind) * PNK_NUMBER dval pn_dval: double value of numeric literal * PNK_TRUE, nullary pn_op: JSOp bytecode * PNK_FALSE, - * PNK_NULL + * PNK_NULL, + * PNK_RAW_UNDEFINED * * PNK_THIS, unary pn_kid: '.this' Name if function `this`, else nullptr * PNK_SUPERBASE unary pn_kid: '.this' Name @@ -686,7 +688,8 @@ class ParseNode isKind(PNK_STRING) || isKind(PNK_TRUE) || isKind(PNK_FALSE) || - isKind(PNK_NULL); + isKind(PNK_NULL) || + isKind(PNK_RAW_UNDEFINED); } /* Return true if this node appears in a Directive Prologue. */ @@ -1141,6 +1144,16 @@ class NullLiteral : public ParseNode explicit NullLiteral(const TokenPos& pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { } }; +// This is only used internally, currently just for tagged templates. +// It represents the value 'undefined' (aka `void 0`), like NullLiteral +// represents the value 'null'. +class RawUndefinedLiteral : public ParseNode +{ + public: + explicit RawUndefinedLiteral(const TokenPos& pos) + : ParseNode(PNK_RAW_UNDEFINED, JSOP_UNDEFINED, PN_NULLARY, pos) { } +}; + class BooleanLiteral : public ParseNode { public: @@ -1361,6 +1374,7 @@ ParseNode::isConstant() case PNK_STRING: case PNK_TEMPLATE_STRING: case PNK_NULL: + case PNK_RAW_UNDEFINED: case PNK_FALSE: case PNK_TRUE: return true; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 66ada79f2..0ff10f73f 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3123,7 +3123,7 @@ template typename ParseHandler::Node Parser::templateLiteral(YieldHandling yieldHandling) { - Node pn = noSubstitutionTemplate(); + Node pn = noSubstitutionUntaggedTemplate(); if (!pn) return null(); @@ -3136,7 +3136,7 @@ Parser::templateLiteral(YieldHandling yieldHandling) if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) return null(); - pn = noSubstitutionTemplate(); + pn = noSubstitutionUntaggedTemplate(); if (!pn) return null(); @@ -3365,7 +3365,7 @@ template bool Parser::appendToCallSiteObj(Node callSiteObj) { - Node cookedNode = noSubstitutionTemplate(); + Node cookedNode = noSubstitutionTaggedTemplate(); if (!cookedNode) return false; @@ -8753,8 +8753,23 @@ Parser::stringLiteral() template typename ParseHandler::Node -Parser::noSubstitutionTemplate() +Parser::noSubstitutionTaggedTemplate() { + if (tokenStream.hasInvalidTemplateEscape()) { + tokenStream.clearInvalidTemplateEscape(); + return handler.newRawUndefinedLiteral(pos()); + } + + return handler.newTemplateStringLiteral(stopStringCompression(), pos()); +} + +template +typename ParseHandler::Node +Parser::noSubstitutionUntaggedTemplate() +{ + if (!tokenStream.checkForInvalidTemplateEscapeError()) + return null(); + return handler.newTemplateStringLiteral(stopStringCompression(), pos()); } @@ -9485,7 +9500,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling return templateLiteral(yieldHandling); case TOK_NO_SUBS_TEMPLATE: - return noSubstitutionTemplate(); + return noSubstitutionUntaggedTemplate(); case TOK_STRING: return stringLiteral(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index e36a6e8b2..b850dc00b 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1052,7 +1052,8 @@ class Parser final : public ParserBase, private JS::AutoGCRooter JSAtom* stopStringCompression(); Node stringLiteral(); - Node noSubstitutionTemplate(); + Node noSubstitutionTaggedTemplate(); + Node noSubstitutionUntaggedTemplate(); Node templateLiteral(YieldHandling yieldHandling); bool taggedTemplate(YieldHandling yieldHandling, Node nodeList, TokenKind tt); bool appendToCallSiteObj(Node callSiteObj); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 9dc3c1072..aa85a631e 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -224,6 +224,7 @@ class SyntaxParseHandler Node newThisLiteral(const TokenPos& pos, Node thisName) { return NodeGeneric; } Node newNullLiteral(const TokenPos& pos) { return NodeGeneric; } + Node newRawUndefinedLiteral(const TokenPos& pos) { return NodeGeneric; } template Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; } diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 186b7a8ab..6a7934e8f 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1917,55 +1917,6 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) return false; } -bool -TokenStream::matchBracedUnicode(bool* matched, uint32_t* cp) -{ - int32_t c; - if (!peekChar(&c)) - return false; - if (c != '{') { - *matched = false; - return true; - } - - consumeKnownChar('{'); - - uint32_t start = userbuf.offset(); - - bool first = true; - uint32_t code = 0; - do { - int32_t c = getCharIgnoreEOL(); - if (c == EOF) { - error(JSMSG_MALFORMED_ESCAPE, "Unicode"); - return false; - } - if (c == '}') { - if (first) { - error(JSMSG_MALFORMED_ESCAPE, "Unicode"); - return false; - } - break; - } - - if (!JS7_ISHEX(c)) { - error(JSMSG_MALFORMED_ESCAPE, "Unicode"); - return false; - } - - code = (code << 4) | JS7_UNHEX(c); - if (code > unicode::NonBMPMax) { - errorAt(start, JSMSG_UNICODE_OVERFLOW, "escape sequence"); - return false; - } - first = false; - } while (true); - - *matched = true; - *cp = code; - return true; -} - bool TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) { @@ -1988,6 +1939,10 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) } if (c == '\\') { + // When parsing templates, we don't immediately report errors for + // invalid escapes; these are handled by the parser. + // In those cases we don't append to tokenbuf, since it won't be + // read. switch (c = getChar()) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; @@ -2003,11 +1958,73 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) // Unicode character specification. case 'u': { - bool matched; - uint32_t code; - if (!matchBracedUnicode(&matched, &code)) + uint32_t code = 0; + + int32_t c2; + if (!peekChar(&c2)) return false; - if (matched) { + + uint32_t start = userbuf.offset() - 2; + + if (c2 == '{') { + consumeKnownChar('{'); + + bool first = true; + bool valid = true; + do { + int32_t c = getCharIgnoreEOL(); + if (c == EOF) { + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + valid = false; + break; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); + return false; + } + if (c == '}') { + if (first) { + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + valid = false; + break; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); + return false; + } + break; + } + + if (!JS7_ISHEX(c)) { + if (parsingTemplate) { + // We put the character back so that we read + // it on the next pass, which matters if it + // was '`' or '\'. + ungetCharIgnoreEOL(c); + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + valid = false; + break; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); + return false; + } + + code = (code << 4) | JS7_UNHEX(c); + if (code > unicode::NonBMPMax) { + if (parsingTemplate) { + setInvalidTemplateEscape(start + 3, InvalidEscapeType::UnicodeOverflow); + valid = false; + break; + } + reportInvalidEscapeError(start + 3, InvalidEscapeType::UnicodeOverflow); + return false; + } + + first = false; + } while (true); + + if (!valid) + continue; MOZ_ASSERT(code <= unicode::NonBMPMax); if (code < unicode::NonBMPMin) { @@ -2030,7 +2047,11 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) c = (c << 4) + JS7_UNHEX(cp[3]); skipChars(4); } else { - error(JSMSG_MALFORMED_ESCAPE, "Unicode"); + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Unicode); + continue; + } + reportInvalidEscapeError(start, InvalidEscapeType::Unicode); return false; } break; @@ -2043,7 +2064,12 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); skipChars(2); } else { - error(JSMSG_MALFORMED_ESCAPE, "hexadecimal"); + uint32_t start = userbuf.offset() - 2; + if (parsingTemplate) { + setInvalidTemplateEscape(start, InvalidEscapeType::Hexadecimal); + continue; + } + reportInvalidEscapeError(start, InvalidEscapeType::Hexadecimal); return false; } break; @@ -2060,8 +2086,8 @@ TokenStream::getStringOrTemplateToken(int untilChar, Token** tp) // Strict mode code allows only \0, then a non-digit. if (val != 0 || JS7_ISDEC(c)) { if (parsingTemplate) { - error(JSMSG_DEPRECATED_OCTAL); - return false; + setInvalidTemplateEscape(userbuf.offset() - 2, InvalidEscapeType::Octal); + continue; } if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL)) return false; diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 4efc31446..fbfefbfe1 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -80,6 +80,20 @@ struct TokenPos { enum DecimalPoint { NoDecimal = false, HasDecimal = true }; +enum class InvalidEscapeType { + // No invalid character escapes. + None, + // A malformed \x escape. + Hexadecimal, + // A malformed \u escape. + Unicode, + // An otherwise well-formed \u escape which represents a + // codepoint > 10FFFF. + UnicodeOverflow, + // An octal escape in a template token. + Octal +}; + class TokenStream; struct Token @@ -361,6 +375,23 @@ class MOZ_STACK_CLASS TokenStream bool hadError() const { return flags.hadError; } void clearSawOctalEscape() { flags.sawOctalEscape = false; } + bool hasInvalidTemplateEscape() const { + return invalidTemplateEscapeType != InvalidEscapeType::None; + } + void clearInvalidTemplateEscape() { + invalidTemplateEscapeType = InvalidEscapeType::None; + } + + // If there is an invalid escape in a template, report it and return false, + // otherwise return true. + bool checkForInvalidTemplateEscapeError() { + if (invalidTemplateEscapeType == InvalidEscapeType::None) + return true; + + reportInvalidEscapeError(invalidTemplateEscapeOffset, invalidTemplateEscapeType); + return false; + } + // TokenStream-specific error reporters. bool reportError(unsigned errorNumber, ...); bool reportErrorNoOffset(unsigned errorNumber, ...); @@ -422,6 +453,33 @@ class MOZ_STACK_CLASS TokenStream bool reportStrictModeError(unsigned errorNumber, ...); bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); } + void setInvalidTemplateEscape(uint32_t offset, InvalidEscapeType type) { + MOZ_ASSERT(type != InvalidEscapeType::None); + if (invalidTemplateEscapeType != InvalidEscapeType::None) + return; + invalidTemplateEscapeOffset = offset; + invalidTemplateEscapeType = type; + } + void reportInvalidEscapeError(uint32_t offset, InvalidEscapeType type) { + switch (type) { + case InvalidEscapeType::None: + MOZ_ASSERT_UNREACHABLE("unexpected InvalidEscapeType"); + return; + case InvalidEscapeType::Hexadecimal: + errorAt(offset, JSMSG_MALFORMED_ESCAPE, "hexadecimal"); + return; + case InvalidEscapeType::Unicode: + errorAt(offset, JSMSG_MALFORMED_ESCAPE, "Unicode"); + return; + case InvalidEscapeType::UnicodeOverflow: + errorAt(offset, JSMSG_UNICODE_OVERFLOW, "escape sequence"); + return; + case InvalidEscapeType::Octal: + errorAt(offset, JSMSG_DEPRECATED_OCTAL); + return; + } + } + static JSAtom* atomize(ExclusiveContext* cx, CharBuffer& cb); MOZ_MUST_USE bool putIdentInTokenbuf(const char16_t* identStart); @@ -442,6 +500,9 @@ class MOZ_STACK_CLASS TokenStream bool awaitIsKeyword = false; friend class AutoAwaitIsKeyword; + uint32_t invalidTemplateEscapeOffset = 0; + InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None; + public: typedef Token::Modifier Modifier; static constexpr Modifier None = Token::None; @@ -955,7 +1016,6 @@ class MOZ_STACK_CLASS TokenStream MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier); - MOZ_MUST_USE bool matchBracedUnicode(bool* matched, uint32_t* code); MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp); int32_t getChar(); -- cgit v1.2.3 From 739a81958035410ddd9b230354a56c909cc5c816 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 18:54:17 -0400 Subject: 1320408 - Part 2: Change JSFunction::getOrCreateScript to static method. --- js/src/frontend/BytecodeCompiler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index b5be5f5ac..a4b7dba6b 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -287,7 +287,8 @@ BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObj RootedObject env(cx, environment); while (env->is() || env->is()) { if (env->is()) { - RootedScript script(cx, env->as().callee().getOrCreateScript(cx)); + RootedFunction fun(cx, &env->as().callee()); + RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun)); if (!script) return false; if (script->argumentsHasVarBinding()) { -- cgit v1.2.3 From c75bd3037aa32d4a2a574feda15f43635bc0c01c Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 01:33:48 -0400 Subject: 1337143 - Tweak NamedImports parsing to eliminate a complexifying peekToken where getToken could be used. --- js/src/frontend/Parser.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0ff10f73f..abb2d51ad 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4663,12 +4663,11 @@ bool Parser::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) { if (tt == TOK_LC) { - TokenStream::Modifier modifier = TokenStream::KeywordIsName; while (true) { // Handle the forms |import {} from 'a'| and // |import { ..., } from 'a'| (where ... is non empty), by // escaping the loop early if the next token is }. - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) return false; if (tt == TOK_RC) @@ -4677,7 +4676,11 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor // If the next token is a keyword, the previous call to // peekToken matched it as a TOK_NAME, and put it in the // lookahead buffer, so this call will match keywords as well. - MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME); + if (tt != TOK_NAME) { + error(JSMSG_NO_IMPORT_NAME); + return false; + } + Rooted importName(context, tokenStream.currentName()); TokenPos importNamePos = pos(); @@ -4735,17 +4738,18 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor handler.addList(importSpecSet, importSpec); - bool matched; - if (!tokenStream.matchToken(&matched, TOK_COMMA)) + TokenKind next; + if (!tokenStream.getToken(&next)) return false; - if (!matched) { - modifier = TokenStream::None; + if (next == TOK_RC) break; + + if (next != TOK_COMMA) { + error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST); + return false; } } - - MUST_MATCH_TOKEN_MOD(TOK_RC, modifier, JSMSG_RC_AFTER_IMPORT_SPEC_LIST); } else { MOZ_ASSERT(tt == TOK_MUL); if (!tokenStream.getToken(&tt)) -- cgit v1.2.3 From d200a2b02e50b33511ff2ea26a2968301f06b170 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 01:35:28 -0400 Subject: 1337143 - Tweak ExportClause parsing to eliminate a peekToken where a simpler consuming getToken could be performed. --- js/src/frontend/Parser.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index abb2d51ad..7e43bc3b5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4990,12 +4990,17 @@ Parser::exportDeclaration() // Handle the forms |export {}| and |export { ..., }| (where ... // is non empty), by escaping the loop early if the next token // is }. - if (!tokenStream.peekToken(&tt)) + if (!tokenStream.getToken(&tt)) return null(); + if (tt == TOK_RC) break; - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); + if (tt != TOK_NAME) { + error(JSMSG_NO_BINDING_NAME); + return null(); + } + Node bindingName = newName(tokenStream.currentName()); if (!bindingName) return null(); @@ -5019,14 +5024,18 @@ Parser::exportDeclaration() handler.addList(kid, exportSpec); - bool matched; - if (!tokenStream.matchToken(&matched, TOK_COMMA)) + TokenKind next; + if (!tokenStream.getToken(&next)) return null(); - if (!matched) + + if (next == TOK_RC) break; - } - MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST); + if (next != TOK_COMMA) { + error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST); + return null(); + } + } // Careful! If |from| follows, even on a new line, it must start a // FromClause: -- cgit v1.2.3 From d27591806950034d8d3763a13db6f2c201311991 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 01:48:29 -0400 Subject: 1339137 - Don't do Annex B lexical function behavior when redeclaring a parameter name in a function with parameter expressions. --- js/src/frontend/Parser.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 7e43bc3b5..2e13910df 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1211,6 +1211,25 @@ Parser::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName n if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind)) return false; + if (!redeclaredKind && pc->isFunctionBox()) { + ParseContext::Scope& funScope = pc->functionScope(); + ParseContext::Scope& varScope = pc->varScope(); + if (&funScope != &varScope) { + // Annex B.3.3.1 disallows redeclaring parameter names. In the + // presence of parameter expressions, parameter names are on the + // function scope, which encloses the var scope. This means + // tryDeclareVar call above would not catch this case, so test it + // manually. + if (AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(name)) { + DeclarationKind declaredKind = p->value()->kind(); + if (DeclarationKindIsParameter(declaredKind)) + redeclaredKind = Some(declaredKind); + else + MOZ_ASSERT(FunctionScope::isSpecialName(context, name)); + } + } + } + if (redeclaredKind) { // If an early error would have occurred, undo all the // VarForAnnexBLexicalFunction declarations. @@ -1751,11 +1770,8 @@ Parser::newFunctionScopeData(ParseContext::Scope& scope, bool case BindingKind::Var: // The only vars in the function scope when there are parameter // exprs, which induces a separate var environment, should be the - // special internal bindings. - MOZ_ASSERT_IF(hasParameterExprs, - bi.name() == context->names().arguments || - bi.name() == context->names().dotThis || - bi.name() == context->names().dotGenerator); + // special bindings. + MOZ_ASSERT_IF(hasParameterExprs, FunctionScope::isSpecialName(context, bi.name())); if (!vars.append(binding)) return Nothing(); break; -- cgit v1.2.3 From 412f15de44cee51e2443bc4846fe9679dca7e1b4 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 14:23:16 -0400 Subject: 1283712 - Part 3: Add Parser::errorWithNotes and Parser::errorWithNotesAt. --- js/src/frontend/BytecodeEmitter.cpp | 7 +++-- js/src/frontend/Parser.cpp | 60 ++++++++++++++++++++++++++++++------- js/src/frontend/Parser.h | 3 ++ js/src/frontend/TokenStream.cpp | 36 ++++++++++++---------- js/src/frontend/TokenStream.h | 11 +++---- 5 files changed, 82 insertions(+), 35 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 44787dc52..7b8b41727 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3604,7 +3604,7 @@ BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportCompileErrorNumberVA(pos.begin, JSREPORT_ERROR, + bool result = tokenStream()->reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR, errorNumber, args); va_end(args); return result; @@ -3617,7 +3617,8 @@ BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportExtraWarningErrorNumberVA(pos.begin, errorNumber, args); + bool result = tokenStream()->reportExtraWarningErrorNumberVA(nullptr, pos.begin, + errorNumber, args); va_end(args); return result; } @@ -3629,7 +3630,7 @@ BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict(), + bool result = tokenStream()->reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(), errorNumber, args); va_end(args); return result; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 2e13910df..923b42870 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -579,7 +579,22 @@ ParserBase::error(unsigned errorNumber, ...) #ifdef DEBUG bool result = #endif - tokenStream.reportCompileErrorNumberVA(pos().begin, JSREPORT_ERROR, errorNumber, args); + tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_ERROR, + errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +void +ParserBase::errorWithNotes(UniquePtr notes, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(Move(notes), pos().begin, JSREPORT_ERROR, + errorNumber, args); MOZ_ASSERT(!result, "reporting an error returned true?"); va_end(args); } @@ -592,7 +607,22 @@ ParserBase::errorAt(uint32_t offset, unsigned errorNumber, ...) #ifdef DEBUG bool result = #endif - tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); + tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args); + MOZ_ASSERT(!result, "reporting an error returned true?"); + va_end(args); +} + +void +ParserBase::errorWithNotesAt(UniquePtr notes, uint32_t offset, + unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); +#ifdef DEBUG + bool result = +#endif + tokenStream.reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_ERROR, + errorNumber, args); MOZ_ASSERT(!result, "reporting an error returned true?"); va_end(args); } @@ -603,7 +633,8 @@ ParserBase::warning(unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); bool result = - tokenStream.reportCompileErrorNumberVA(pos().begin, JSREPORT_WARNING, errorNumber, args); + tokenStream.reportCompileErrorNumberVA(nullptr, pos().begin, JSREPORT_WARNING, + errorNumber, args); va_end(args); return result; } @@ -614,7 +645,8 @@ ParserBase::warningAt(uint32_t offset, unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); bool result = - tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); + tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING, + errorNumber, args); va_end(args); return result; } @@ -624,7 +656,8 @@ ParserBase::extraWarning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = tokenStream.reportExtraWarningErrorNumberVA(pos().begin, errorNumber, args); + bool result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, pos().begin, + errorNumber, args); va_end(args); return result; } @@ -635,7 +668,7 @@ ParserBase::strictModeError(unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); bool res = - tokenStream.reportStrictModeErrorNumberVA(pos().begin, pc->sc()->strict(), + tokenStream.reportStrictModeErrorNumberVA(nullptr, pos().begin, pc->sc()->strict(), errorNumber, args); va_end(args); return res; @@ -647,7 +680,8 @@ ParserBase::strictModeErrorAt(uint32_t offset, unsigned errorNumber, ...) va_list args; va_start(args, errorNumber); bool res = - tokenStream.reportStrictModeErrorNumberVA(offset, pc->sc()->strict(), errorNumber, args); + tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, pc->sc()->strict(), + errorNumber, args); va_end(args); return res; } @@ -661,17 +695,21 @@ ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumb uint32_t offset = TokenStream::NoOffset; switch (kind) { case ParseError: - result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); + result = tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, + errorNumber, args); break; case ParseWarning: result = - tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_WARNING, errorNumber, args); + tokenStream.reportCompileErrorNumberVA(nullptr, offset, JSREPORT_WARNING, + errorNumber, args); break; case ParseExtraWarning: - result = tokenStream.reportExtraWarningErrorNumberVA(offset, errorNumber, args); + result = tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, + errorNumber, args); break; case ParseStrictError: - result = tokenStream.reportStrictModeErrorNumberVA(offset, strict, errorNumber, args); + result = tokenStream.reportStrictModeErrorNumberVA(nullptr, offset, strict, + errorNumber, args); break; } va_end(args); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b850dc00b..35b9384a8 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -824,9 +824,12 @@ class ParserBase : public StrictModeGetter /* Report the given error at the current offset. */ void error(unsigned errorNumber, ...); + void errorWithNotes(UniquePtr notes, unsigned errorNumber, ...); /* Report the given error at the given offset. */ void errorAt(uint32_t offset, unsigned errorNumber, ...); + void errorWithNotesAt(UniquePtr notes, uint32_t offset, + unsigned errorNumber, ...); /* * Handle a strict mode error at the current offset. Report an error if in diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 6a7934e8f..76ea7e821 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -607,8 +607,8 @@ TokenStream::seek(const Position& pos, const TokenStream& other) } bool -TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber, - va_list args) +TokenStream::reportStrictModeErrorNumberVA(UniquePtr notes, uint32_t offset, + bool strictMode, unsigned errorNumber, va_list args) { // In strict mode code, this is an error, not merely a warning. unsigned flags; @@ -619,7 +619,7 @@ TokenStream::reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, uns else return true; - return reportCompileErrorNumberVA(offset, flags, errorNumber, args); + return reportCompileErrorNumberVA(Move(notes), offset, flags, errorNumber, args); } void @@ -645,8 +645,8 @@ CompileError::throwError(JSContext* cx) } bool -TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber, - va_list args) +TokenStream::reportCompileErrorNumberVA(UniquePtr notes, uint32_t offset, + unsigned flags, unsigned errorNumber, va_list args) { bool warning = JSREPORT_IS_WARNING(flags); @@ -663,6 +663,7 @@ TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigne return false; CompileError& err = *tempErrPtr; + err.notes = Move(notes); err.flags = flags; err.errorNumber = errorNumber; err.filename = filename; @@ -754,7 +755,7 @@ TokenStream::reportStrictModeError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportStrictModeErrorNumberVA(currentToken().pos.begin, strictMode(), + bool result = reportStrictModeErrorNumberVA(nullptr, currentToken().pos.begin, strictMode(), errorNumber, args); va_end(args); return result; @@ -765,8 +766,8 @@ TokenStream::reportError(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_ERROR, errorNumber, - args); + bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR, + errorNumber, args); va_end(args); return result; } @@ -776,8 +777,8 @@ TokenStream::reportErrorNoOffset(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportCompileErrorNumberVA(NoOffset, JSREPORT_ERROR, errorNumber, - args); + bool result = reportCompileErrorNumberVA(nullptr, NoOffset, JSREPORT_ERROR, + errorNumber, args); va_end(args); return result; } @@ -787,19 +788,21 @@ TokenStream::warning(unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_WARNING, + bool result = reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_WARNING, errorNumber, args); va_end(args); return result; } bool -TokenStream::reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args) +TokenStream::reportExtraWarningErrorNumberVA(UniquePtr notes, uint32_t offset, + unsigned errorNumber, va_list args) { if (!options().extraWarningsOption) return true; - return reportCompileErrorNumberVA(offset, JSREPORT_STRICT|JSREPORT_WARNING, errorNumber, args); + return reportCompileErrorNumberVA(Move(notes), offset, JSREPORT_STRICT|JSREPORT_WARNING, + errorNumber, args); } void @@ -810,7 +813,7 @@ TokenStream::reportAsmJSError(uint32_t offset, unsigned errorNumber, ...) unsigned flags = options().throwOnAsmJSValidationFailureOption ? JSREPORT_ERROR : JSREPORT_WARNING; - reportCompileErrorNumberVA(offset, flags, errorNumber, args); + reportCompileErrorNumberVA(nullptr, offset, flags, errorNumber, args); va_end(args); } @@ -822,7 +825,8 @@ TokenStream::error(unsigned errorNumber, ...) #ifdef DEBUG bool result = #endif - reportCompileErrorNumberVA(currentToken().pos.begin, JSREPORT_ERROR, errorNumber, args); + reportCompileErrorNumberVA(nullptr, currentToken().pos.begin, JSREPORT_ERROR, + errorNumber, args); MOZ_ASSERT(!result, "reporting an error returned true?"); va_end(args); } @@ -835,7 +839,7 @@ TokenStream::errorAt(uint32_t offset, unsigned errorNumber, ...) #ifdef DEBUG bool result = #endif - reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args); + reportCompileErrorNumberVA(nullptr, offset, JSREPORT_ERROR, errorNumber, args); MOZ_ASSERT(!result, "reporting an error returned true?"); va_end(args); } diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index fbfefbfe1..e0119c83d 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -410,11 +410,12 @@ class MOZ_STACK_CLASS TokenStream // General-purpose error reporters. You should avoid calling these // directly, and instead use the more succinct alternatives (error(), // warning(), &c.) in TokenStream, Parser, and BytecodeEmitter. - bool reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber, - va_list args); - bool reportStrictModeErrorNumberVA(uint32_t offset, bool strictMode, unsigned errorNumber, - va_list args); - bool reportExtraWarningErrorNumberVA(uint32_t offset, unsigned errorNumber, va_list args); + bool reportCompileErrorNumberVA(UniquePtr notes, uint32_t offset, unsigned flags, + unsigned errorNumber, va_list args); + bool reportStrictModeErrorNumberVA(UniquePtr notes, uint32_t offset, + bool strictMode, unsigned errorNumber, va_list args); + bool reportExtraWarningErrorNumberVA(UniquePtr notes, uint32_t offset, + unsigned errorNumber, va_list args); // asm.js reporter void reportAsmJSError(uint32_t offset, unsigned errorNumber, ...); -- cgit v1.2.3 From 05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 16:14:01 -0400 Subject: 1336783 - Part 1: Rework on reserved word and remove TokenStream::KeywordIsName. --- js/src/frontend/GenerateReservedWords.py | 213 +++++++++ js/src/frontend/Parser.cpp | 716 +++++++++++++------------------ js/src/frontend/Parser.h | 48 ++- js/src/frontend/ReservedWords.h | 81 ++++ js/src/frontend/TokenKind.h | 99 ++++- js/src/frontend/TokenStream.cpp | 191 +++++---- js/src/frontend/TokenStream.h | 135 ++---- 7 files changed, 858 insertions(+), 625 deletions(-) create mode 100644 js/src/frontend/GenerateReservedWords.py create mode 100644 js/src/frontend/ReservedWords.h (limited to 'js/src/frontend') diff --git a/js/src/frontend/GenerateReservedWords.py b/js/src/frontend/GenerateReservedWords.py new file mode 100644 index 000000000..bd698cc5f --- /dev/null +++ b/js/src/frontend/GenerateReservedWords.py @@ -0,0 +1,213 @@ +# 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/. + +import re +import sys + +def read_reserved_word_list(filename): + macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$") + + reserved_word_list = [] + index = 0 + with open(filename, 'r') as f: + for line in f: + m = macro_pat.search(line) + if m: + reserved_word_list.append((index, m.group(1))) + index += 1 + + assert(len(reserved_word_list) != 0) + + return reserved_word_list + +def line(opt, s): + opt['output'].write('{}{}\n'.format(' ' * opt['indent_level'], s)) + +def indent(opt): + opt['indent_level'] += 1 + +def dedent(opt): + opt['indent_level'] -= 1 + +def span_and_count_at(reserved_word_list, column): + assert(len(reserved_word_list) != 0); + + chars_dict = {} + for index, word in reserved_word_list: + chars_dict[ord(word[column])] = True + + chars = sorted(chars_dict.keys()) + return chars[-1] - chars[0] + 1, len(chars) + +def optimal_switch_column(opt, reserved_word_list, columns, unprocessed_columns): + assert(len(reserved_word_list) != 0); + assert(unprocessed_columns != 0); + + min_count = 0 + min_span = 0 + min_count_index = 0 + min_span_index = 0 + + for index in range(0, unprocessed_columns): + span, count = span_and_count_at(reserved_word_list, columns[index]) + assert(span != 0) + + if span == 1: + assert(count == 1) + return 1, True + + assert(count != 1) + if index == 0 or min_span > span: + min_span = span + min_span_index = index + + if index == 0 or min_count > count: + min_count = count + min_count_index = index + + if min_count <= opt['use_if_threshold']: + return min_count_index, True + + return min_span_index, False + +def split_list_per_column(reserved_word_list, column): + assert(len(reserved_word_list) != 0); + + column_dict = {} + for item in reserved_word_list: + index, word = item + per_column = column_dict.setdefault(word[column], []) + per_column.append(item) + + return sorted(column_dict.items(), key=lambda (char, word): ord(char)) + +def generate_letter_switch(opt, unprocessed_columns, reserved_word_list, + columns=None): + assert(len(reserved_word_list) != 0); + + if not columns: + columns = range(0, unprocessed_columns) + + if len(reserved_word_list) == 1: + index, word = reserved_word_list[0] + + if unprocessed_columns == 0: + line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word)) + return + + if unprocessed_columns > opt['char_tail_test_threshold']: + line(opt, 'JSRW_TEST_GUESS({}) /* {} */'.format(index, word)) + return + + conds = [] + for column in columns[0:unprocessed_columns]: + quoted = repr(word[column]) + conds.append('JSRW_AT({})=={}'.format(column, quoted)) + + line(opt, 'if ({}) {{'.format(' && '.join(conds))) + + indent(opt) + line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word)) + dedent(opt) + + line(opt, '}') + line(opt, 'JSRW_NO_MATCH()') + return + + assert(unprocessed_columns != 0); + + optimal_column_index, use_if = optimal_switch_column(opt, reserved_word_list, + columns, + unprocessed_columns) + optimal_column = columns[optimal_column_index] + + # Make a copy to avoid breaking passed list. + columns = columns[:] + columns[optimal_column_index] = columns[unprocessed_columns - 1] + + list_per_column = split_list_per_column(reserved_word_list, optimal_column) + + if not use_if: + line(opt, 'switch (JSRW_AT({})) {{'.format(optimal_column)) + + for char, reserved_word_list_per_column in list_per_column: + quoted = repr(char) + if use_if: + line(opt, 'if (JSRW_AT({}) == {}) {{'.format(optimal_column, + quoted)) + else: + line(opt, ' case {}:'.format(quoted)) + + indent(opt) + generate_letter_switch(opt, unprocessed_columns - 1, + reserved_word_list_per_column, columns) + dedent(opt) + + if use_if: + line(opt, '}') + + if not use_if: + line(opt, '}') + + line(opt, 'JSRW_NO_MATCH()') + +def split_list_per_length(reserved_word_list): + assert(len(reserved_word_list) != 0); + + length_dict = {} + for item in reserved_word_list: + index, word = item + per_length = length_dict.setdefault(len(word), []) + per_length.append(item) + + return sorted(length_dict.items(), key=lambda (length, word): length) + +def generate_switch(opt, reserved_word_list): + assert(len(reserved_word_list) != 0); + + line(opt, '/*') + line(opt, ' * Generating switch for the list of {} entries:'.format(len(reserved_word_list))) + for index, word in reserved_word_list: + line(opt, ' * {}'.format(word)) + line(opt, ' */') + + list_per_length = split_list_per_length(reserved_word_list) + + use_if = False + if len(list_per_length) < opt['use_if_threshold']: + use_if = True + + if not use_if: + line(opt, 'switch (JSRW_LENGTH()) {') + + for length, reserved_word_list_per_length in list_per_length: + if use_if: + line(opt, 'if (JSRW_LENGTH() == {}) {{'.format(length)) + else: + line(opt, ' case {}:'.format(length)) + + indent(opt) + generate_letter_switch(opt, length, reserved_word_list_per_length) + dedent(opt) + + if use_if: + line(opt, '}') + + if not use_if: + line(opt, '}') + line(opt, 'JSRW_NO_MATCH()') + +def main(output, reserved_words_h): + reserved_word_list = read_reserved_word_list(reserved_words_h) + + opt = { + 'indent_level': 1, + 'use_if_threshold': 3, + 'char_tail_test_threshold': 4, + 'output': output + } + generate_switch(opt, reserved_word_list) + +if __name__ == '__main__': + main(sys.stdout, *sys.argv[1:]) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 923b42870..9b057fb72 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -62,19 +62,27 @@ using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr; using BindingIter = ParseContext::Scope::BindingIter; using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr; -/* Read a token. Report an error and return null() if that token isn't of type tt. */ -#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \ +// Read a token. Report an error and return null() if that token doesn't match +// to the given func's condition. +#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \ JS_BEGIN_MACRO \ TokenKind token; \ if (!tokenStream.getToken(&token, modifier)) \ return null(); \ - if (token != tt) { \ + if (!(func)(token)) { \ error(errorNumber); \ return null(); \ } \ JS_END_MACRO -#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errno) +#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \ + MUST_MATCH_TOKEN_FUNC_MOD([](TokenKind tok) { return tok == tt; }, modifier, errorNumber) + +#define MUST_MATCH_TOKEN(tt, errorNumber) \ + MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber) + +#define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \ + MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber) template static inline void @@ -753,7 +761,8 @@ ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, checkOptionsCalled(false), #endif abortedSyntaxParse(false), - isUnexpectedEOF_(false) + isUnexpectedEOF_(false), + awaitIsKeyword_(false) { cx->perThreadData->frontendCollectionPool.addActiveCompilation(); tempPoolMark = alloc.mark(); @@ -810,6 +819,22 @@ Parser::~Parser() MOZ_ASSERT(checkOptionsCalled); } +template <> +void +Parser::setAwaitIsKeyword(bool isKeyword) +{ + awaitIsKeyword_ = isKeyword; +} + +template <> +void +Parser::setAwaitIsKeyword(bool isKeyword) +{ + awaitIsKeyword_ = isKeyword; + if (Parser* parser = handler.syntaxParser) + parser->setAwaitIsKeyword(isKeyword); +} + template ObjectBox* Parser::newObjectBox(JSObject* obj) @@ -936,7 +961,7 @@ Parser::parse() /* * Strict mode forbids introducing new definitions for 'eval', 'arguments', or - * for any strict mode reserved keyword. + * for any strict mode reserved word. */ bool ParserBase::isValidStrictBinding(PropertyName* name) @@ -945,7 +970,8 @@ ParserBase::isValidStrictBinding(PropertyName* name) name != context->names().arguments && name != context->names().let && name != context->names().static_ && - !(IsKeyword(name) && name != context->names().await); + name != context->names().yield && + !IsStrictReservedWord(name); } /* @@ -959,13 +985,30 @@ Parser::checkStrictBinding(PropertyName* name, TokenPos pos) if (!pc->sc()->needStrictChecks()) return true; - if (!isValidStrictBinding(name)) { - JSAutoByteString bytes; - if (!AtomToPrintableString(context, name, &bytes)) - return false; - return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, bytes.ptr()); + if (name == context->names().arguments) + return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, "arguments"); + + if (name == context->names().eval) + return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, "eval"); + + if (name == context->names().let) { + errorAt(pos.begin, JSMSG_RESERVED_ID, "let"); + return false; } + if (name == context->names().static_) { + errorAt(pos.begin, JSMSG_RESERVED_ID, "static"); + return false; + } + + if (name == context->names().yield) { + errorAt(pos.begin, JSMSG_RESERVED_ID, "yield"); + return false; + } + + if (IsStrictReservedWord(name)) + return strictModeErrorAt(pos.begin, JSMSG_RESERVED_ID, ReservedWordToCharZ(name)); + return true; } @@ -2094,7 +2137,7 @@ Parser::moduleBody(ModuleSharedContext* modulesc) if (!mn) return null(); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true); + AutoAwaitIsKeyword awaitIsKeyword(this, true); ParseNode* pn = statementList(YieldIsKeyword); if (!pn) return null(); @@ -2417,7 +2460,7 @@ Parser::standaloneFunction(HandleFunction fun, funpc.setIsStandaloneFunctionBody(); YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction); + AutoAwaitIsKeyword awaitIsKeyword(this, asyncKind == AsyncFunction); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, parameterListEnd, /* isStandaloneFunction = */ true)) { @@ -2719,10 +2762,7 @@ Parser::matchOrInsertSemicolonHelper(TokenStream::Modifier modifie * 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) - { + if (!pc->isAsync() && tokenStream.currentToken().type == TOK_AWAIT) { error(JSMSG_AWAIT_OUTSIDE_ASYNC); return false; } @@ -2845,7 +2885,7 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand; if (!tokenStream.peekToken(&tt, firstTokenModifier)) return false; - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { parenFreeArrow = true; argModifier = firstTokenModifier; } @@ -2901,7 +2941,7 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt, argModifier)) return false; argModifier = TokenStream::Operand; - MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD); + MOZ_ASSERT_IF(parenFreeArrow, TokenKindIsPossibleIdentifier(tt)); if (tt == TOK_TRIPLEDOT) { if (IsSetterKind(kind)) { @@ -2922,7 +2962,7 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!tokenStream.getToken(&tt)) return false; - if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) { + if (!TokenKindIsPossibleIdentifier(tt) && tt != TOK_LB && tt != TOK_LC) { error(JSMSG_NO_REST_NAME); return false; } @@ -2952,20 +2992,15 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn break; } - case TOK_NAME: - case TOK_YIELD: { - if (parenFreeArrow) - funbox->setStart(tokenStream); - - if (funbox->isAsync() && tokenStream.currentName() == context->names().await) { - // `await` is already gotten as TOK_NAME for the following - // case: - // - // async await => 1 - error(JSMSG_RESERVED_ID, "await"); + default: { + if (!TokenKindIsPossibleIdentifier(tt)) { + error(JSMSG_MISSING_FORMAL); return false; } + if (parenFreeArrow) + funbox->setStart(tokenStream); + RootedPropertyName name(context, bindingIdentifier(yieldHandling)); if (!name) return false; @@ -2980,10 +3015,6 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn break; } - - default: - error(JSMSG_MISSING_FORMAL); - return false; } if (positionalFormals.length() >= ARGNO_LIMIT) { @@ -3507,7 +3538,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, FunctionBox* funbox = pc->functionBox(); RootedFunction fun(context, funbox->function()); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync()); + AutoAwaitIsKeyword awaitIsKeyword(this, funbox->isAsync()); if (!functionArguments(yieldHandling, kind, pn)) return false; @@ -3656,7 +3687,7 @@ Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHan } RootedPropertyName name(context); - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); @@ -3712,7 +3743,7 @@ Parser::functionExpr(uint32_t preludeStart, InvokedPrediction invo { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); - AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction); + AutoAwaitIsKeyword awaitIsKeyword(this, asyncKind == AsyncFunction); GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) @@ -3731,7 +3762,7 @@ Parser::functionExpr(uint32_t preludeStart, InvokedPrediction invo YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); RootedPropertyName name(context); - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); @@ -3768,18 +3799,6 @@ IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str) return pos.begin + str->length() + 2 == pos.end; } -template -bool -Parser::checkUnescapedName() -{ - const Token& token = tokenStream.currentToken(); - if (!token.nameContainsEscape()) - return true; - - errorAt(token.pos.begin, JSMSG_ESCAPED_KEYWORD); - return false; -} - template <> bool Parser::asmJS(Node list) @@ -3998,7 +4017,7 @@ Parser::matchLabel(YieldHandling yieldHandling, MutableHandle::declarationName(Node decl, DeclarationKind declKind, Token ParseNodeKind* forHeadKind, Node* forInOrOfExpression) { // Anything other than TOK_YIELD or TOK_NAME is an error. - if (tt != TOK_NAME && tt != TOK_YIELD) { + // Anything other than possible identifier is an error. + if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_NO_VARIABLE_NAME); return null(); } @@ -4721,16 +4741,13 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor // Handle the forms |import {} from 'a'| and // |import { ..., } from 'a'| (where ... is non empty), by // escaping the loop early if the next token is }. - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return false; if (tt == TOK_RC) break; - // If the next token is a keyword, the previous call to - // peekToken matched it as a TOK_NAME, and put it in the - // lookahead buffer, so this call will match keywords as well. - if (tt != TOK_NAME) { + if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_IMPORT_NAME); return false; } @@ -4738,23 +4755,16 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor Rooted importName(context, tokenStream.currentName()); TokenPos importNamePos = pos(); - TokenKind maybeAs; - if (!tokenStream.peekToken(&maybeAs)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_AS)) return null(); - if (maybeAs == TOK_NAME && - tokenStream.nextName() == context->names().as) - { - tokenStream.consumeKnownToken(TOK_NAME); - - if (!checkUnescapedName()) - return false; - + if (matched) { TokenKind afterAs; if (!tokenStream.getToken(&afterAs)) return false; - if (afterAs != TOK_NAME && afterAs != TOK_YIELD) { + if (!TokenKindIsPossibleIdentifierName(afterAs)) { error(JSMSG_NO_BINDING_NAME); return false; } @@ -4764,10 +4774,7 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor // by the keyword 'as'. // See the ImportSpecifier production in ES6 section 15.2.2. if (IsKeyword(importName)) { - JSAutoByteString bytes; - if (!AtomToPrintableString(context, importName, &bytes)) - return false; - error(JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr()); + error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName)); return false; } } @@ -4806,18 +4813,10 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor } } else { MOZ_ASSERT(tt == TOK_MUL); - if (!tokenStream.getToken(&tt)) - return false; - if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) { - error(JSMSG_AS_AFTER_IMPORT_STAR); - return false; - } + MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR); - if (!checkUnescapedName()) - return false; - - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME); + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME); Node importName = newName(context->names().star); if (!importName) @@ -4878,8 +4877,15 @@ Parser::importDeclaration() if (!importSpecSet) return null(); - if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) { - if (tt == TOK_NAME) { + if (tt == TOK_STRING) { + // Handle the form |import 'a'| by leaving the list empty. This is + // equivalent to |import {} from 'a'|. + importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; + } else { + if (tt == TOK_LC || tt == TOK_MUL) { + if (!namedImportsOrNamespaceImport(tt, importSpecSet)) + return null(); + } else if (TokenKindIsPossibleIdentifierName(tt)) { // Handle the form |import a from 'b'|, by adding a single import // specifier to the list, with 'default' as the import name and // 'a' as the binding name. This is equivalent to @@ -4922,29 +4928,19 @@ Parser::importDeclaration() return null(); } } else { - if (!namedImportsOrNamespaceImport(tt, importSpecSet)) - return null(); + error(JSMSG_DECLARATION_AFTER_IMPORT); + return null(); } if (!tokenStream.getToken(&tt)) return null(); - if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { + if (tt != TOK_FROM) { error(JSMSG_FROM_AFTER_IMPORT_CLAUSE); return null(); } - if (!checkUnescapedName()) - return null(); - MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); - } else if (tt == TOK_STRING) { - // Handle the form |import 'a'| by leaving the list empty. This is - // equivalent to |import {} from 'a'|. - importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin; - } else { - error(JSMSG_DECLARATION_AFTER_IMPORT); - return null(); } Node moduleSpec = stringLiteral(); @@ -5050,7 +5046,7 @@ Parser::exportDeclaration() if (tt == TOK_RC) break; - if (tt != TOK_NAME) { + if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_BINDING_NAME); return null(); } @@ -5060,10 +5056,10 @@ Parser::exportDeclaration() return null(); bool foundAs; - if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as)) + if (!tokenStream.matchToken(&foundAs, TOK_AS)) return null(); if (foundAs) - MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_EXPORT_NAME); + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME); Node exportName = newName(tokenStream.currentName()); if (!exportName) @@ -5098,21 +5094,18 @@ Parser::exportDeclaration() // from "foo"; // a single ExportDeclaration // // But if it doesn't, we might have an ASI opportunity in Operand - // context, so simply matching a contextual keyword won't work: + // context: // // export { x } // ExportDeclaration, terminated by ASI // fro\u006D // ExpressionStatement, the name "from" // // In that case let matchOrInsertSemicolonAfterNonExpression sort out // ASI or any necessary error. - TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::Operand)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand)) return null(); - if (tt == TOK_NAME && - tokenStream.currentToken().name() == context->names().from && - !tokenStream.currentToken().nameContainsEscape()) - { + if (matched) { MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); Node moduleSpec = stringLiteral(); @@ -5129,8 +5122,6 @@ Parser::exportDeclaration() return node; } - tokenStream.ungetToken(); - if (!matchOrInsertSemicolonAfterNonExpression()) return null(); break; @@ -5151,14 +5142,11 @@ Parser::exportDeclaration() if (!tokenStream.getToken(&tt)) return null(); - if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) { + if (tt != TOK_FROM) { error(JSMSG_FROM_AFTER_EXPORT_STAR); return null(); } - if (!checkUnescapedName()) - return null(); - MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); Node moduleSpec = stringLiteral(); @@ -5227,10 +5215,7 @@ Parser::exportDeclaration() return null(); break; default: { - if (tt == TOK_NAME && - tokenStream.currentName() == context->names().async && - !tokenStream.currentToken().nameContainsEscape()) - { + if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); @@ -5276,19 +5261,13 @@ Parser::exportDeclaration() return null(); break; - case TOK_NAME: - if (tokenStream.currentName() == context->names().let) { - if (!checkUnescapedName()) - return null(); - - kid = lexicalDeclaration(YieldIsName, /* isConst = */ false); - if (!kid) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; - } - MOZ_FALLTHROUGH; + case TOK_LET: + kid = lexicalDeclaration(YieldIsName, /* isConst = */ false); + if (!kid) + return null(); + if (!checkExportedNamesForDeclaration(kid)) + return null(); + break; default: error(JSMSG_DECLARATION_AFTER_EXPORT); @@ -5338,10 +5317,7 @@ Parser::consequentOrAlternative(YieldHandling yieldHandling) // // Careful! FunctionDeclaration doesn't include generators or async // functions. - if (next == TOK_NAME && - !tokenStream.nextNameContainsEscape() && - tokenStream.nextName() == context->names().async) - { + if (next == TOK_ASYNC) { tokenStream.consumeKnownToken(next, TokenStream::Operand); // Peek only on the same line: ExpressionStatement's lookahead @@ -5514,13 +5490,9 @@ Parser::matchInOrOf(bool* isForInp, bool* isForOfp) return false; *isForInp = tt == TOK_IN; - *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of; - if (!*isForInp && !*isForOfp) { + *isForOfp = tt == TOK_OF; + if (!*isForInp && !*isForOfp) tokenStream.ungetToken(); - } else { - if (tt == TOK_NAME && !checkUnescapedName()) - return false; - } MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp); return true; @@ -5570,15 +5542,12 @@ Parser::forHeadStart(YieldHandling yieldHandling, if (tt == TOK_CONST) { parsingLexicalDeclaration = true; tokenStream.consumeKnownToken(tt, TokenStream::Operand); - } else if (tt == TOK_NAME && - tokenStream.nextName() == context->names().let && - !tokenStream.nextNameContainsEscape()) - { + } else if (tt == TOK_LET) { // We could have a {For,Lexical}Declaration, or we could have a // LeftHandSideExpression with lookahead restrictions so it's not // ambiguous with the former. Check for a continuation of the former // to decide which we have. - tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand); + tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand); TokenKind next; if (!tokenStream.peekToken(&next)) @@ -5699,7 +5668,7 @@ Parser::forStatement(YieldHandling yieldHandling) if (allowsForEachIn()) { bool matched; - if (!tokenStream.matchContextualKeyword(&matched, context->names().each)) + if (!tokenStream.matchToken(&matched, TOK_EACH)) return null(); if (matched) { iflags = JSITER_FOREACH; @@ -6410,12 +6379,12 @@ Parser::tryStatement(YieldHandling yieldHandling) * kid3 is the finally statement * * catch nodes are ternary. - * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) + * kid1 is the lvalue (possible identifier, TOK_LB, or TOK_LC) * kid2 is the catch guard or null if no guard * kid3 is the catch block * * catch lvalue nodes are either: - * TOK_NAME for a single identifier + * a single identifier * TOK_RB or TOK_RC for a destructuring left-hand side * * finally nodes are TOK_LC statement lists. @@ -6490,8 +6459,12 @@ Parser::tryStatement(YieldHandling yieldHandling) return null(); break; - case TOK_NAME: - case TOK_YIELD: { + default: { + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_CATCH_IDENTIFIER); + return null(); + } + RootedPropertyName param(context, bindingIdentifier(yieldHandling)); if (!param) return null(); @@ -6502,10 +6475,6 @@ Parser::tryStatement(YieldHandling yieldHandling) return null(); break; } - - default: - error(JSMSG_CATCH_IDENTIFIER); - return null(); } Node catchGuard = null(); @@ -6664,7 +6633,7 @@ Parser::classDefinition(YieldHandling yieldHandling, return null(); RootedPropertyName name(context); - if (tt == TOK_NAME || tt == TOK_YIELD) { + if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) return null(); @@ -6721,7 +6690,7 @@ Parser::classDefinition(YieldHandling yieldHandling, bool seenConstructor = false; for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_RC) break; @@ -6730,22 +6699,18 @@ Parser::classDefinition(YieldHandling yieldHandling, continue; bool isStatic = false; - if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) { - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + if (tt == TOK_STATIC) { + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) { - tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(tt); error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); } if (tt != TOK_LP) { - if (!checkUnescapedName()) - return null(); - isStatic = true; } else { - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); tokenStream.ungetToken(); } } else { @@ -6865,9 +6830,7 @@ template bool Parser::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling) { - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME)); - MOZ_ASSERT(tokenStream.currentName() == context->names().let); - MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape()); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET)); #ifdef DEBUG TokenKind verify; @@ -6879,18 +6842,19 @@ Parser::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand if (next == TOK_LB || next == TOK_LC) return true; - // Otherwise a let declaration must have a name. - if (next == TOK_NAME) { - if (tokenStream.nextName() == context->names().yield) { - MOZ_ASSERT(tokenStream.nextNameContainsEscape(), - "token stream should interpret unescaped 'yield' as TOK_YIELD"); - - // Same as |next == TOK_YIELD|. - return yieldHandling == YieldIsName; - } + // If we have the name "yield", the grammar parameter exactly states + // whether this is okay. (This wasn't true for SpiderMonkey's ancient + // legacy generator syntax, but that's dead now.) If YieldIsName, + // declaration-parsing code will (if necessary) enforce a strict mode + // restriction on defining "yield". If YieldIsKeyword, consider this the + // end of the declaration, in case ASI induces a semicolon that makes the + // "yield" valid. + if (next == TOK_YIELD) + return yieldHandling == YieldIsName; - // One non-"yield" TOK_NAME edge case deserves special comment. - // Consider this: + // Otherwise a let declaration must have a name. + if (TokenKindIsPossibleIdentifier(next)) { + // A "let" edge case deserves special comment. Consider this: // // let // not an ASI opportunity // let; @@ -6903,16 +6867,6 @@ Parser::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand return true; } - // If we have the name "yield", the grammar parameter exactly states - // whether this is okay. (This wasn't true for SpiderMonkey's ancient - // legacy generator syntax, but that's dead now.) If YieldIsName, - // declaration-parsing code will (if necessary) enforce a strict mode - // restriction on defining "yield". If YieldIsKeyword, consider this the - // end of the declaration, in case ASI induces a semicolon that makes the - // "yield" valid. - if (next == TOK_YIELD) - return yieldHandling == YieldIsName; - // Otherwise not a let declaration. return false; } @@ -6975,16 +6929,21 @@ Parser::statement(YieldHandling yieldHandling) return expressionStatement(yieldHandling); } - case TOK_NAME: { + default: { + // Avoid getting next token with None. + if (tt == TOK_AWAIT && pc->isAsync()) + return expressionStatement(yieldHandling); + + if (!TokenKindIsPossibleIdentifier(tt)) + return expressionStatement(yieldHandling); + TokenKind next; if (!tokenStream.peekToken(&next)) return null(); // |let| here can only be an Identifier, not a declaration. Give nicer // errors for declaration-looking typos. - if (!tokenStream.currentToken().nameContainsEscape() && - tokenStream.currentName() == context->names().let) - { + if (tt == TOK_LET) { bool forbiddenLetDeclaration = false; if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) { @@ -6994,7 +6953,7 @@ Parser::statement(YieldHandling yieldHandling) } else if (next == TOK_LB) { // Enforce ExpressionStatement's 'let [' lookahead restriction. forbiddenLetDeclaration = true; - } else if (next == TOK_LC || next == TOK_NAME) { + } else if (next == TOK_LC || TokenKindIsPossibleIdentifier(next)) { // 'let {' and 'let foo' aren't completely forbidden, if ASI // causes 'let' to be the entire Statement. But if they're // same-line, we can aggressively give a better error message. @@ -7005,7 +6964,7 @@ Parser::statement(YieldHandling yieldHandling) if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); - MOZ_ASSERT(nextSameLine == TOK_NAME || + MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TOK_LC || nextSameLine == TOK_EOL); @@ -7029,9 +6988,6 @@ Parser::statement(YieldHandling yieldHandling) case TOK_NEW: return expressionStatement(yieldHandling, PredictInvoked); - default: - return expressionStatement(yieldHandling); - // IfStatement[?Yield, ?Return] case TOK_IF: return ifStatement(yieldHandling); @@ -7077,7 +7033,7 @@ Parser::statement(YieldHandling yieldHandling) return withStatement(yieldHandling); // LabelledStatement[?Yield, ?Return] - // This is really handled by TOK_NAME and TOK_YIELD cases above. + // This is really handled by default and TOK_YIELD cases above. // ThrowStatement[?Yield] case TOK_THROW: @@ -7183,28 +7139,29 @@ Parser::statementListItem(YieldHandling yieldHandling, return expressionStatement(yieldHandling); } - case TOK_NAME: { + default: { + // Avoid getting next token with None. + if (tt == TOK_AWAIT && pc->isAsync()) + return expressionStatement(yieldHandling); + + if (!TokenKindIsPossibleIdentifier(tt)) + return expressionStatement(yieldHandling); + TokenKind next; if (!tokenStream.peekToken(&next)) return null(); - if (!tokenStream.currentToken().nameContainsEscape()) { - if (tokenStream.currentName() == context->names().let && - nextTokenContinuesLetDeclaration(next, yieldHandling)) - { - return lexicalDeclaration(yieldHandling, /* isConst = */ false); - } + if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next, yieldHandling)) + return lexicalDeclaration(yieldHandling, /* isConst = */ false); - if (tokenStream.currentName() == context->names().async) { - TokenKind nextSameLine = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&nextSameLine)) { - return null(); - } - if (nextSameLine == TOK_FUNCTION) { - uint32_t preludeStart = pos().begin; - tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction); - } + if (tt == TOK_ASYNC) { + TokenKind nextSameLine = TOK_EOF; + if (!tokenStream.peekTokenSameLine(&nextSameLine)) + return null(); + if (nextSameLine == TOK_FUNCTION) { + uint32_t preludeStart = pos().begin; + tokenStream.consumeKnownToken(TOK_FUNCTION); + return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction); } } @@ -7217,9 +7174,6 @@ Parser::statementListItem(YieldHandling yieldHandling, case TOK_NEW: return expressionStatement(yieldHandling, PredictInvoked); - default: - return expressionStatement(yieldHandling); - // IfStatement[?Yield, ?Return] case TOK_IF: return ifStatement(yieldHandling); @@ -7265,7 +7219,7 @@ Parser::statementListItem(YieldHandling yieldHandling, return withStatement(yieldHandling); // LabelledStatement[?Yield, ?Return] - // This is really handled by TOK_NAME and TOK_YIELD cases above. + // This is really handled by default and TOK_YIELD cases above. // ThrowStatement[?Yield] case TOK_THROW: @@ -7631,6 +7585,9 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl bool endsExpr; + // This only handles identifiers that *never* have special meaning anywhere + // in the language. Contextual keywords, reserved words in strict mode, + // and other hard cases are handled outside this fast path. if (tt == TOK_NAME) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) return null(); @@ -7661,15 +7618,12 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl return yieldExpression(inHandling); bool maybeAsyncArrow = false; - if (tt == TOK_NAME && - tokenStream.currentName() == context->names().async && - !tokenStream.currentToken().nameContainsEscape()) - { + if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); - if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD) + if (TokenKindIsPossibleIdentifier(nextSameLine)) maybeAsyncArrow = true; } @@ -7683,14 +7637,12 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl PossibleError possibleErrorInner(*this); Node lhs; if (maybeAsyncArrow) { - tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand); - MOZ_ASSERT(tokenStream.currentName() == context->names().async); - MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape()); + tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand); TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); - MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD); + MOZ_ASSERT(TokenKindIsPossibleIdentifier(tt)); // Check yield validity here. RootedPropertyName name(context, bindingIdentifier(yieldHandling)); @@ -7759,24 +7711,18 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl GeneratorKind generatorKind = NotGenerator; FunctionAsyncKind asyncKind = SyncFunction; - if (next == TOK_NAME) { + if (next == TOK_ASYNC) { tokenStream.consumeKnownToken(next, TokenStream::Operand); - if (tokenStream.currentName() == context->names().async && - !tokenStream.currentToken().nameContainsEscape()) - { - TokenKind nextSameLine = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&nextSameLine)) - return null(); + TokenKind nextSameLine = TOK_EOF; + if (!tokenStream.peekTokenSameLine(&nextSameLine)) + return null(); - if (nextSameLine == TOK_ARROW) { - tokenStream.ungetToken(); - } else { - generatorKind = StarGenerator; - asyncKind = AsyncFunction; - } - } else { + if (nextSameLine == TOK_ARROW) { tokenStream.ungetToken(); + } else { + generatorKind = StarGenerator; + asyncKind = AsyncFunction; } } @@ -8025,20 +7971,17 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t } case TOK_AWAIT: { - if (!pc->isAsync()) { - // TOK_AWAIT can be returned in module, even if it's not inside - // async function. - error(JSMSG_RESERVED_ID, "await"); - return null(); + if (pc->isAsync()) { + Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); + if (!kid) + return null(); + pc->lastAwaitOffset = begin; + return newAwaitExpression(begin, kid); } - - Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); - if (!kid) - return null(); - pc->lastAwaitOffset = begin; - return newAwaitExpression(begin, kid); } + MOZ_FALLTHROUGH; + default: { Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, possibleError, invoked); @@ -8176,7 +8119,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) // FIXME: Destructuring binding (bug 980828). - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME); + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME); RootedPropertyName name(context, tokenStream.currentName()); if (name == context->names().let) { error(JSMSG_LET_COMP_BINDING); @@ -8187,7 +8130,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) if (!lhs) return null(); bool matched; - if (!tokenStream.matchContextualKeyword(&matched, context->names().of)) + if (!tokenStream.matchToken(&matched, TOK_OF)) return null(); if (!matched) { error(JSMSG_OF_AFTER_FOR_NAME); @@ -8522,9 +8465,9 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling Node nextMember; if (tt == TOK_DOT) { - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); - if (tt == TOK_NAME) { + if (TokenKindIsPossibleIdentifierName(tt)) { PropertyName* field = tokenStream.currentName(); if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) { error(JSMSG_BAD_SUPERPROP, "property"); @@ -8695,46 +8638,52 @@ Parser::newName(PropertyName* name, TokenPos pos) template PropertyName* -Parser::labelOrIdentifierReference(YieldHandling yieldHandling, - bool yieldTokenizedAsName) -{ - PropertyName* ident; - bool isYield; - const Token& tok = tokenStream.currentToken(); - if (tok.type == TOK_NAME) { - MOZ_ASSERT(tok.name() != context->names().yield || - tok.nameContainsEscape() || - yieldTokenizedAsName, - "tokenizer should have treated unescaped 'yield' as TOK_YIELD"); - MOZ_ASSERT_IF(yieldTokenizedAsName, tok.name() == context->names().yield); - - ident = tok.name(); - isYield = ident == context->names().yield; - } else { - MOZ_ASSERT(tok.type == TOK_YIELD && !yieldTokenizedAsName); - - ident = context->names().yield; - isYield = true; - } - - if (!isYield) { - if (pc->sc()->strict()) { - const char* badName = ident == context->names().let - ? "let" - : ident == context->names().static_ - ? "static" - : nullptr; - if (badName) { - error(JSMSG_RESERVED_ID, badName); - return nullptr; - } - } - } else { +Parser::checkLabelOrIdentifierReference(PropertyName* ident, + uint32_t offset, + YieldHandling yieldHandling) +{ + if (ident == context->names().yield) { if (yieldHandling == YieldIsKeyword || pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) { - error(JSMSG_RESERVED_ID, "yield"); + errorAt(offset, JSMSG_RESERVED_ID, "yield"); + return nullptr; + } + return ident; + } + + if (ident == context->names().await) { + if (awaitIsKeyword()) { + errorAt(offset, JSMSG_RESERVED_ID, "await"); + return nullptr; + } + return ident; + } + + if (IsKeyword(ident) || IsReservedWordLiteral(ident)) { + errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(ident)); + return nullptr; + } + + if (IsFutureReservedWord(ident)) { + errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)); + return nullptr; + } + + if (pc->sc()->strict()) { + if (IsStrictReservedWord(ident)) { + errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)); + return nullptr; + } + + if (ident == context->names().let) { + errorAt(offset, JSMSG_RESERVED_ID, "let"); + return nullptr; + } + + if (ident == context->names().static_) { + errorAt(offset, JSMSG_RESERVED_ID, "static"); return nullptr; } } @@ -8744,52 +8693,34 @@ Parser::labelOrIdentifierReference(YieldHandling yieldHandling, template PropertyName* -Parser::bindingIdentifier(YieldHandling yieldHandling) +Parser::labelOrIdentifierReference(YieldHandling yieldHandling) { - PropertyName* ident; - bool isYield; - const Token& tok = tokenStream.currentToken(); - if (tok.type == TOK_NAME) { - MOZ_ASSERT(tok.name() != context->names().yield || tok.nameContainsEscape(), - "tokenizer should have treated unescaped 'yield' as TOK_YIELD"); - - ident = tok.name(); - isYield = ident == context->names().yield; - } else { - MOZ_ASSERT(tok.type == TOK_YIELD); + // ES 2017 draft 12.1.1. + // StringValue of IdentifierName normalizes any Unicode escape sequences + // in IdentifierName hence such escapes cannot be used to write an + // Identifier whose code point sequence is the same as a ReservedWord. + // + // Use PropertyName* instead of TokenKind to reflect the normalization. - ident = context->names().yield; - isYield = true; - } + return checkLabelOrIdentifierReference(tokenStream.currentName(), pos().begin, yieldHandling); +} - if (!isYield) { - if (pc->sc()->strict()) { - const char* badName = ident == context->names().arguments - ? "arguments" - : ident == context->names().eval - ? "eval" - : nullptr; - if (badName) { - error(JSMSG_BAD_STRICT_ASSIGN, badName); - return nullptr; - } +template +PropertyName* +Parser::bindingIdentifier(YieldHandling yieldHandling) +{ + PropertyName* ident = labelOrIdentifierReference(yieldHandling); + if (!ident) + return nullptr; - badName = ident == context->names().let - ? "let" - : ident == context->names().static_ - ? "static" - : nullptr; - if (badName) { - error(JSMSG_RESERVED_ID, badName); - return nullptr; - } + if (pc->sc()->strict()) { + if (ident == context->names().arguments) { + error(JSMSG_BAD_STRICT_ASSIGN, "arguments"); + return nullptr; } - } else { - if (yieldHandling == YieldIsKeyword || - pc->sc()->strict() || - versionNumber() >= JSVERSION_1_7) - { - error(JSMSG_RESERVED_ID, "yield"); + + if (ident == context->names().eval) { + error(JSMSG_BAD_STRICT_ASSIGN, "eval"); return nullptr; } } @@ -8970,7 +8901,7 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, PropertyType* propType, MutableHandleAtom propAtom) { TokenKind ltok; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(<ok)) return null(); MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC"); @@ -8979,14 +8910,11 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, bool isAsync = false; if (ltok == TOK_MUL) { isGenerator = true; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(<ok)) return null(); } - if (ltok == TOK_NAME && - tokenStream.currentName() == context->names().async && - !tokenStream.currentToken().nameContainsEscape()) - { + if (ltok == TOK_ASYNC) { // AsyncMethod[Yield, Await]: // async [no LineTerminator here] PropertyName[?Yield, ?Await] ... // @@ -9002,16 +8930,13 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, // ComputedPropertyName[Yield, Await]: // [ ... TokenKind tt = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); - if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB || - tt == TOK_NAME || tt == TOK_YIELD) - { + if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) { isAsync = true; - tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName); ltok = tt; } else { - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); + tokenStream.ungetToken(); } } @@ -9038,41 +8963,36 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return null(); break; - case TOK_NAME: { + default: { + if (!TokenKindIsPossibleIdentifierName(ltok)) { + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok)); + return null(); + } + propAtom.set(tokenStream.currentName()); // Do not look for accessor syntax on generators - if (isGenerator || isAsync || - !(propAtom.get() == context->names().get || - propAtom.get() == context->names().set)) - { + if (isGenerator || isAsync || !(ltok == TOK_GET || ltok == TOK_SET)) { propName = handler.newObjectLiteralPropertyName(propAtom, pos()); if (!propName) return null(); break; } - *propType = propAtom.get() == context->names().get ? PropertyType::Getter - : PropertyType::Setter; + *propType = ltok == TOK_GET ? PropertyType::Getter : PropertyType::Setter; // We have parsed |get| or |set|. Look for an accessor property // name next. TokenKind tt; - if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.peekToken(&tt)) return null(); - if (tt == TOK_NAME) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_NAME, TokenStream::KeywordIsName); + if (TokenKindIsPossibleIdentifierName(tt)) { + tokenStream.consumeKnownToken(tt); propAtom.set(tokenStream.currentName()); return handler.newObjectLiteralPropertyName(propAtom, pos()); } if (tt == TOK_STRING) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_STRING, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(TOK_STRING); propAtom.set(tokenStream.currentToken().atom()); @@ -9086,10 +9006,7 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return stringLiteral(); } if (tt == TOK_NUMBER) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_NUMBER, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(TOK_NUMBER); propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number())); if (!propAtom.get()) @@ -9097,10 +9014,7 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return newNumber(tokenStream.currentToken()); } if (tt == TOK_LB) { - if (!checkUnescapedName()) - return null(); - - tokenStream.consumeKnownToken(TOK_LB, TokenStream::KeywordIsName); + tokenStream.consumeKnownToken(TOK_LB); return computedPropertyName(yieldHandling, propList); } @@ -9109,7 +9023,6 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos()); if (!propName) return null(); - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); break; } @@ -9127,10 +9040,6 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return null(); break; } - - default: - error(JSMSG_BAD_PROP_ID); - return null(); } TokenKind tt; @@ -9146,7 +9055,9 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, return propName; } - if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) { + if (TokenKindIsPossibleIdentifierName(ltok) && + (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) + { if (isGenerator) { error(JSMSG_BAD_PROP_ID); return null(); @@ -9214,7 +9125,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* RootedAtom propAtom(context); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_RC) break; @@ -9274,17 +9185,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer * shorthand for |var o = {x: x, y: y}|. */ - TokenKind propToken = TOK_NAME; - if (!tokenStream.checkForKeyword(propAtom, &propToken)) - return null(); - - if (propToken != TOK_NAME && propToken != TOK_YIELD) { - error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); - return null(); - } - - Rooted name(context, - identifierReference(yieldHandling, propToken == TOK_YIELD)); + Rooted name(context, identifierReference(yieldHandling)); if (!name) return null(); @@ -9299,17 +9200,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand * with default values, as per ES6 12.14.5 */ - TokenKind propToken = TOK_NAME; - if (!tokenStream.checkForKeyword(propAtom, &propToken)) - return null(); - - if (propToken != TOK_NAME && propToken != TOK_YIELD) { - error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken)); - return null(); - } - - Rooted name(context, - identifierReference(yieldHandling, propToken == TOK_YIELD)); + Rooted name(context, identifierReference(yieldHandling)); if (!name) return null(); @@ -9482,14 +9373,11 @@ Parser::tryNewTarget(Node &newTarget) if (!tokenStream.getToken(&next)) return false; - if (next != TOK_NAME || tokenStream.currentName() != context->names().target) { + if (next != TOK_TARGET) { error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next)); return false; } - if (!checkUnescapedName()) - return false; - if (!pc->sc()->allowNewTarget()) { errorAt(begin, JSMSG_BAD_NEWTARGET); return false; @@ -9572,11 +9460,13 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling case TOK_STRING: return stringLiteral(); - case TOK_YIELD: - case TOK_NAME: { - if (tokenStream.currentName() == context->names().async && - !tokenStream.currentToken().nameContainsEscape()) - { + default: { + if (!TokenKindIsPossibleIdentifier(tt)) { + error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); + return null(); + } + + if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); @@ -9648,7 +9538,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling // the enclosing code is strict mode code, any of "let", "yield", // or "arguments" should be prohibited. Argument-parsing code // handles that. - if (next != TOK_NAME && next != TOK_YIELD) { + if (!TokenKindIsPossibleIdentifier(next)) { error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next)); return null(); } @@ -9675,10 +9565,6 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling // Return an arbitrary expression node. See case TOK_RP above. return handler.newNullLiteral(pos()); } - - default: - error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); - return null(); } } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 35b9384a8..9ec862d92 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -734,6 +734,9 @@ class UsedNameTracker } }; +template +class AutoAwaitIsKeyword; + class ParserBase : public StrictModeGetter { private: @@ -783,7 +786,13 @@ class ParserBase : public StrictModeGetter /* Unexpected end of input, i.e. TOK_EOF not at top-level. */ bool isUnexpectedEOF_:1; + bool awaitIsKeyword_:1; + public: + bool awaitIsKeyword() const { + return awaitIsKeyword_; + } + ParserBase(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options, const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames, Parser* syntaxParser, @@ -998,6 +1007,9 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Parser* syntaxParser, LazyScript* lazyOuterFunction); ~Parser(); + friend class AutoAwaitIsKeyword; + void setAwaitIsKeyword(bool isKeyword); + bool checkOptions(); // A Parser::Mark is the extension of the LifoAlloc::Mark to the entire @@ -1047,8 +1059,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter void trace(JSTracer* trc); - bool checkUnescapedName(); - private: Parser* thisForCtor() { return this; } @@ -1321,17 +1331,18 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling); - PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling, - bool yieldTokenizedAsName); + PropertyName* checkLabelOrIdentifierReference(PropertyName* ident, + uint32_t offset, + YieldHandling yieldHandling); + + PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling); PropertyName* labelIdentifier(YieldHandling yieldHandling) { - return labelOrIdentifierReference(yieldHandling, false); + return labelOrIdentifierReference(yieldHandling); } - PropertyName* identifierReference(YieldHandling yieldHandling, - bool yieldTokenizedAsName = false) - { - return labelOrIdentifierReference(yieldHandling, yieldTokenizedAsName); + PropertyName* identifierReference(YieldHandling yieldHandling) { + return labelOrIdentifierReference(yieldHandling); } PropertyName* importedBinding() { @@ -1455,6 +1466,25 @@ class Parser final : public ParserBase, private JS::AutoGCRooter bool asmJS(Node list); }; +template +class MOZ_STACK_CLASS AutoAwaitIsKeyword +{ + private: + Parser* parser_; + bool oldAwaitIsKeyword_; + + public: + AutoAwaitIsKeyword(Parser* parser, bool awaitIsKeyword) { + parser_ = parser; + oldAwaitIsKeyword_ = parser_->awaitIsKeyword_; + parser_->setAwaitIsKeyword(awaitIsKeyword); + } + + ~AutoAwaitIsKeyword() { + parser_->setAwaitIsKeyword(oldAwaitIsKeyword_); + } +}; + } /* namespace frontend */ } /* namespace js */ diff --git a/js/src/frontend/ReservedWords.h b/js/src/frontend/ReservedWords.h new file mode 100644 index 000000000..27f5b11c1 --- /dev/null +++ b/js/src/frontend/ReservedWords.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +/* A higher-order macro for enumerating reserved word tokens. */ + +#ifndef vm_ReservedWords_h +#define vm_ReservedWords_h + +#define FOR_EACH_JAVASCRIPT_RESERVED_WORD(macro) \ + macro(false, false_, TOK_FALSE) \ + macro(true, true_, TOK_TRUE) \ + macro(null, null, TOK_NULL) \ + \ + /* Keywords. */ \ + macro(break, break_, TOK_BREAK) \ + macro(case, case_, TOK_CASE) \ + macro(catch, catch_, TOK_CATCH) \ + macro(const, const_, TOK_CONST) \ + macro(continue, continue_, TOK_CONTINUE) \ + macro(debugger, debugger, TOK_DEBUGGER) \ + macro(default, default_, TOK_DEFAULT) \ + macro(delete, delete_, TOK_DELETE) \ + macro(do, do_, TOK_DO) \ + macro(else, else_, TOK_ELSE) \ + macro(finally, finally_, TOK_FINALLY) \ + macro(for, for_, TOK_FOR) \ + macro(function, function, TOK_FUNCTION) \ + macro(if, if_, TOK_IF) \ + macro(in, in, TOK_IN) \ + macro(instanceof, instanceof, TOK_INSTANCEOF) \ + macro(new, new_, TOK_NEW) \ + macro(return, return_, TOK_RETURN) \ + macro(switch, switch_, TOK_SWITCH) \ + macro(this, this_, TOK_THIS) \ + macro(throw, throw_, TOK_THROW) \ + macro(try, try_, TOK_TRY) \ + macro(typeof, typeof_, TOK_TYPEOF) \ + macro(var, var, TOK_VAR) \ + macro(void, void_, TOK_VOID) \ + macro(while, while_, TOK_WHILE) \ + macro(with, with, TOK_WITH) \ + macro(import, import, TOK_IMPORT) \ + macro(export, export_, TOK_EXPORT) \ + macro(class, class_, TOK_CLASS) \ + macro(extends, extends, TOK_EXTENDS) \ + macro(super, super, TOK_SUPER) \ + \ + /* Future reserved words. */ \ + macro(enum, enum_, TOK_ENUM) \ + \ + /* Future reserved words, but only in strict mode. */ \ + macro(implements, implements, TOK_IMPLEMENTS) \ + macro(interface, interface, TOK_INTERFACE) \ + macro(package, package, TOK_PACKAGE) \ + macro(private, private_, TOK_PRIVATE) \ + macro(protected, protected_, TOK_PROTECTED) \ + macro(public, public_, TOK_PUBLIC) \ + \ + /* Contextual keywords. */ \ + macro(as, as, TOK_AS) \ + macro(async, async, TOK_ASYNC) \ + macro(await, await, TOK_AWAIT) \ + macro(each, each, TOK_EACH) \ + macro(from, from, TOK_FROM) \ + macro(get, get, TOK_GET) \ + macro(let, let, TOK_LET) \ + macro(of, of, TOK_OF) \ + macro(set, set, TOK_SET) \ + macro(static, static_, TOK_STATIC) \ + macro(target, target, TOK_TARGET) \ + /* \ + * Yield is a token inside function*. Outside of a function*, it is a \ + * future reserved word in strict mode, but a keyword in JS1.7 even \ + * when strict. Punt logic to parser. \ + */ \ + macro(yield, yield, TOK_YIELD) + +#endif /* vm_ReservedWords_h */ diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h index 6f22d78e5..98f23fec8 100644 --- a/js/src/frontend/TokenKind.h +++ b/js/src/frontend/TokenKind.h @@ -81,9 +81,12 @@ \ macro(REGEXP, "regular expression literal") \ macro(TRUE, "boolean literal 'true'") \ + range(RESERVED_WORD_LITERAL_FIRST, TRUE) \ macro(FALSE, "boolean literal 'false'") \ macro(NULL, "null literal") \ + range(RESERVED_WORD_LITERAL_LAST, NULL) \ macro(THIS, "keyword 'this'") \ + range(KEYWORD_FIRST, THIS) \ macro(FUNCTION, "keyword 'function'") \ macro(IF, "keyword 'if'") \ macro(ELSE, "keyword 'else'") \ @@ -106,16 +109,43 @@ macro(FINALLY, "keyword 'finally'") \ macro(THROW, "keyword 'throw'") \ macro(DEBUGGER, "keyword 'debugger'") \ - macro(YIELD, "keyword 'yield'") \ - macro(AWAIT, "keyword 'await'") \ macro(EXPORT, "keyword 'export'") \ macro(IMPORT, "keyword 'import'") \ macro(CLASS, "keyword 'class'") \ macro(EXTENDS, "keyword 'extends'") \ macro(SUPER, "keyword 'super'") \ - macro(RESERVED, "reserved keyword") \ - /* reserved keywords in strict mode */ \ - macro(STRICT_RESERVED, "reserved keyword") \ + range(KEYWORD_LAST, SUPER) \ + \ + /* contextual keywords */ \ + macro(AS, "'as'") \ + range(CONTEXTUAL_KEYWORD_FIRST, AS) \ + macro(ASYNC, "'async'") \ + macro(AWAIT, "'await'") \ + macro(EACH, "'each'") \ + macro(FROM, "'from'") \ + macro(GET, "'get'") \ + macro(LET, "'let'") \ + macro(OF, "'of'") \ + macro(SET, "'set'") \ + macro(STATIC, "'static'") \ + macro(TARGET, "'target'") \ + macro(YIELD, "'yield'") \ + range(CONTEXTUAL_KEYWORD_LAST, YIELD) \ + \ + /* future reserved words */ \ + macro(ENUM, "reserved word 'enum'") \ + range(FUTURE_RESERVED_KEYWORD_FIRST, ENUM) \ + range(FUTURE_RESERVED_KEYWORD_LAST, ENUM) \ + \ + /* reserved words in strict mode */ \ + macro(IMPLEMENTS, "reserved word 'implements'") \ + range(STRICT_RESERVED_KEYWORD_FIRST, IMPLEMENTS) \ + macro(INTERFACE, "reserved word 'interface'") \ + macro(PACKAGE, "reserved word 'package'") \ + macro(PRIVATE, "reserved word 'private'") \ + macro(PROTECTED, "reserved word 'protected'") \ + macro(PUBLIC, "reserved word 'public'") \ + range(STRICT_RESERVED_KEYWORD_LAST, PUBLIC) \ \ /* \ * The following token types occupy contiguous ranges to enable easy \ @@ -149,7 +179,9 @@ range(RELOP_LAST, GE) \ \ macro(INSTANCEOF, "keyword 'instanceof'") \ + range(KEYWORD_BINOP_FIRST, INSTANCEOF) \ macro(IN, "keyword 'in'") \ + range(KEYWORD_BINOP_LAST, IN) \ \ /* Shift ops, per TokenKindIsShift. */ \ macro(LSH, "'<<'") \ @@ -168,7 +200,9 @@ \ /* Unary operation tokens. */ \ macro(TYPEOF, "keyword 'typeof'") \ + range(KEYWORD_UNOP_FIRST, TYPEOF) \ macro(VOID, "keyword 'void'") \ + range(KEYWORD_UNOP_LAST, VOID) \ macro(NOT, "'!'") \ macro(BITNOT, "'~'") \ \ @@ -239,6 +273,61 @@ TokenKindIsAssignment(TokenKind tt) return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST; } +inline MOZ_MUST_USE bool +TokenKindIsKeyword(TokenKind tt) +{ + return (TOK_KEYWORD_FIRST <= tt && tt <= TOK_KEYWORD_LAST) || + (TOK_KEYWORD_BINOP_FIRST <= tt && tt <= TOK_KEYWORD_BINOP_LAST) || + (TOK_KEYWORD_UNOP_FIRST <= tt && tt <= TOK_KEYWORD_UNOP_LAST); +} + +inline MOZ_MUST_USE bool +TokenKindIsContextualKeyword(TokenKind tt) +{ + return TOK_CONTEXTUAL_KEYWORD_FIRST <= tt && tt <= TOK_CONTEXTUAL_KEYWORD_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsFutureReservedWord(TokenKind tt) +{ + return TOK_FUTURE_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_FUTURE_RESERVED_KEYWORD_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsStrictReservedWord(TokenKind tt) +{ + return TOK_STRICT_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_STRICT_RESERVED_KEYWORD_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsReservedWordLiteral(TokenKind tt) +{ + return TOK_RESERVED_WORD_LITERAL_FIRST <= tt && tt <= TOK_RESERVED_WORD_LITERAL_LAST; +} + +inline MOZ_MUST_USE bool +TokenKindIsReservedWord(TokenKind tt) +{ + return TokenKindIsKeyword(tt) || + TokenKindIsFutureReservedWord(tt) || + TokenKindIsReservedWordLiteral(tt); +} + +inline MOZ_MUST_USE bool +TokenKindIsPossibleIdentifier(TokenKind tt) +{ + return tt == TOK_NAME || + TokenKindIsContextualKeyword(tt) || + TokenKindIsStrictReservedWord(tt); +} + +inline MOZ_MUST_USE bool +TokenKindIsPossibleIdentifierName(TokenKind tt) +{ + return TokenKindIsPossibleIdentifier(tt) || + TokenKindIsReservedWord(tt); +} + } // namespace frontend } // namespace js diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 76ea7e821..b8623d545 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -24,10 +24,10 @@ #include "jsnum.h" #include "frontend/BytecodeCompiler.h" +#include "frontend/ReservedWords.h" #include "js/CharacterEncoding.h" #include "js/UniquePtr.h" #include "vm/HelperThreads.h" -#include "vm/Keywords.h" #include "vm/StringBuffer.h" #include "vm/Unicode.h" @@ -40,65 +40,65 @@ using mozilla::PodAssign; using mozilla::PodCopy; using mozilla::PodZero; -struct KeywordInfo { - const char* chars; // C string with keyword text +struct ReservedWordInfo { + const char* chars; // C string with reserved word text TokenKind tokentype; }; -static const KeywordInfo keywords[] = { -#define KEYWORD_INFO(keyword, name, type) \ - {js_##keyword##_str, type}, - FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_INFO) -#undef KEYWORD_INFO +static const ReservedWordInfo reservedWords[] = { +#define RESERVED_WORD_INFO(word, name, type) \ + {js_##word##_str, type}, + FOR_EACH_JAVASCRIPT_RESERVED_WORD(RESERVED_WORD_INFO) +#undef RESERVED_WORD_INFO }; -// Returns a KeywordInfo for the specified characters, or nullptr if the string -// is not a keyword. +// Returns a ReservedWordInfo for the specified characters, or nullptr if the +// string is not a reserved word. template -static const KeywordInfo* -FindKeyword(const CharT* s, size_t length) +static const ReservedWordInfo* +FindReservedWord(const CharT* s, size_t length) { MOZ_ASSERT(length != 0); size_t i; - const KeywordInfo* kw; + const ReservedWordInfo* rw; const char* chars; -#define JSKW_LENGTH() length -#define JSKW_AT(column) s[column] -#define JSKW_GOT_MATCH(index) i = (index); goto got_match; -#define JSKW_TEST_GUESS(index) i = (index); goto test_guess; -#define JSKW_NO_MATCH() goto no_match; -#include "jsautokw.h" -#undef JSKW_NO_MATCH -#undef JSKW_TEST_GUESS -#undef JSKW_GOT_MATCH -#undef JSKW_AT -#undef JSKW_LENGTH +#define JSRW_LENGTH() length +#define JSRW_AT(column) s[column] +#define JSRW_GOT_MATCH(index) i = (index); goto got_match; +#define JSRW_TEST_GUESS(index) i = (index); goto test_guess; +#define JSRW_NO_MATCH() goto no_match; +#include "frontend/ReservedWordsGenerated.h" +#undef JSRW_NO_MATCH +#undef JSRW_TEST_GUESS +#undef JSRW_GOT_MATCH +#undef JSRW_AT +#undef JSRW_LENGTH got_match: - return &keywords[i]; + return &reservedWords[i]; test_guess: - kw = &keywords[i]; - chars = kw->chars; + rw = &reservedWords[i]; + chars = rw->chars; do { if (*s++ != (unsigned char)(*chars++)) goto no_match; } while (--length != 0); - return kw; + return rw; no_match: return nullptr; } -static const KeywordInfo* -FindKeyword(JSLinearString* str) +static const ReservedWordInfo* +FindReservedWord(JSLinearString* str) { JS::AutoCheckCannotGC nogc; return str->hasLatin1Chars() - ? FindKeyword(str->latin1Chars(nogc), str->length()) - : FindKeyword(str->twoByteChars(nogc), str->length()); + ? FindReservedWord(str->latin1Chars(nogc), str->length()) + : FindReservedWord(str->twoByteChars(nogc), str->length()); } template @@ -188,7 +188,68 @@ frontend::IsIdentifier(const char16_t* chars, size_t length) bool frontend::IsKeyword(JSLinearString* str) { - return FindKeyword(str) != nullptr; + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsKeyword(rw->tokentype); + + return false; +} + +bool +frontend::IsFutureReservedWord(JSLinearString* str) +{ + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsFutureReservedWord(rw->tokentype); + + return false; +} + +bool +frontend::IsStrictReservedWord(JSLinearString* str) +{ + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsStrictReservedWord(rw->tokentype); + + return false; +} + +bool +frontend::IsReservedWordLiteral(JSLinearString* str) +{ + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return TokenKindIsReservedWordLiteral(rw->tokentype); + + return false; +} + +const char* +frontend::ReservedWordToCharZ(PropertyName* str) +{ + const ReservedWordInfo* rw = FindReservedWord(str); + if (rw == nullptr) + return nullptr; + + switch (rw->tokentype) { +#define EMIT_CASE(word, name, type) case type: return js_##word##_str; + FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE) +#undef EMIT_CASE + default: + MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName."); + } + return nullptr; +} + +PropertyName* +TokenStream::reservedWordToPropertyName(TokenKind tt) const +{ + MOZ_ASSERT(tt != TOK_NAME); + switch (tt) { +#define EMIT_CASE(word, name, type) case type: return cx->names().name; + FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE) +#undef EMIT_CASE + default: + MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind."); + } + return nullptr; } TokenStream::SourceCoords::SourceCoords(ExclusiveContext* cx, uint32_t ln) @@ -1169,38 +1230,6 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart) return true; } -bool -TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp) -{ - if (!awaitIsKeyword && kw->tokentype == TOK_AWAIT) { - if (ttp) - *ttp = TOK_NAME; - return true; - } - - if (kw->tokentype == TOK_RESERVED) { - error(JSMSG_RESERVED_ID, kw->chars); - return false; - } - - if (kw->tokentype == TOK_STRICT_RESERVED) - return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars); - - // Working keyword. - *ttp = kw->tokentype; - return true; -} - -bool -TokenStream::checkForKeyword(JSAtom* atom, TokenKind* ttp) -{ - const KeywordInfo* kw = FindKeyword(atom); - if (!kw) - return true; - - return checkForKeyword(kw, ttp); -} - enum FirstCharKind { // A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid // token that cannot also be a prefix of a longer token. E.g. ';' has the @@ -1424,36 +1453,18 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) length = userbuf.addressOfNextRawChar() - identStart; } - // Represent keywords as keyword tokens unless told otherwise. - if (modifier != KeywordIsName) { - if (const KeywordInfo* kw = FindKeyword(chars, length)) { - // That said, keywords can't contain escapes. (Contexts where - // keywords are treated as names, that also sometimes treat - // keywords as keywords, must manually check this requirement.) - // There are two exceptions - // 1) StrictReservedWords: These keywords need to be treated as - // names in non-strict mode. - // 2) yield is also treated as a name if it contains an escape - // sequence. The parser must handle this case separately. - if (hadUnicodeEscape && !( - (kw->tokentype == TOK_STRICT_RESERVED && !strictMode()) || - kw->tokentype == TOK_YIELD)) - { - reportError(JSMSG_ESCAPED_KEYWORD); - goto error; - } - - tp->type = TOK_NAME; - if (!checkForKeyword(kw, &tp->type)) - goto error; - if (tp->type != TOK_NAME && !hadUnicodeEscape) - goto out; + // Represent reserved words as reserved word tokens. + if (!hadUnicodeEscape) { + if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) { + tp->type = rw->tokentype; + goto out; } } JSAtom* atom = AtomizeChars(cx, chars, length); - if (!atom) + if (!atom) { goto error; + } tp->type = TOK_NAME; tp->setName(atom->asPropertyName()); goto out; diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index e0119c83d..e92de4b03 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -26,14 +26,13 @@ #include "js/UniquePtr.h" #include "js/Vector.h" #include "vm/RegExpObject.h" +#include "vm/String.h" struct KeywordInfo; namespace js { namespace frontend { -class AutoAwaitIsKeyword; - struct TokenPos { uint32_t begin; // Offset of the token's first char. uint32_t end; // Offset of 1 past the token's last char. @@ -120,9 +119,6 @@ struct Token // TOK_DIV. Operand, - // Treat keywords as names by returning TOK_NAME. - KeywordIsName, - // Treat subsequent characters as the tail of a template literal, after // a template substitution, beginning with a "}", continuing with zero // or more template literal characters, and ending with either "${" or @@ -164,10 +160,6 @@ struct Token // If a semicolon is inserted automatically, the next token is already // gotten with None, but we expect Operand. OperandIsNone, - - // If name of method definition is `get` or `set`, the next token is - // already gotten with KeywordIsName, but we expect None. - NoneIsKeywordIsName, }; friend class TokenStream; @@ -224,11 +216,6 @@ struct Token return u.name->JSAtom::asPropertyName(); // poor-man's type verification } - bool nameContainsEscape() const { - PropertyName* n = name(); - return pos.begin + n->length() != pos.end; - } - JSAtom* atom() const { MOZ_ASSERT(type == TOK_STRING || type == TOK_TEMPLATE_HEAD || @@ -254,10 +241,22 @@ struct Token }; class CompileError : public JSErrorReport { -public: + public: void throwError(JSContext* cx); }; +extern const char* +ReservedWordToCharZ(PropertyName* str); + +extern MOZ_MUST_USE bool +IsFutureReservedWord(JSLinearString* str); + +extern MOZ_MUST_USE bool +IsReservedWordLiteral(JSLinearString* str); + +extern MOZ_MUST_USE bool +IsStrictReservedWord(JSLinearString* str); + // Ideally, tokenizing would be entirely independent of context. But the // strict mode flag, which is in SharedContext, affects tokenizing, and // TokenStream needs to see it. @@ -344,25 +343,26 @@ class MOZ_STACK_CLASS TokenStream JSVersion versionNumber() const { return VersionNumber(options().version); } JSVersion versionWithFlags() const { return options().version; } + private: + PropertyName* reservedWordToPropertyName(TokenKind tt) const; + + public: PropertyName* currentName() const { - if (isCurrentTokenType(TOK_YIELD)) - return cx->names().yield; - MOZ_ASSERT(isCurrentTokenType(TOK_NAME)); - return currentToken().name(); + if (isCurrentTokenType(TOK_NAME)) { + return currentToken().name(); + } + + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type)); + return reservedWordToPropertyName(currentToken().type); } PropertyName* nextName() const { - if (nextToken().type == TOK_YIELD) - return cx->names().yield; - MOZ_ASSERT(nextToken().type == TOK_NAME); - return nextToken().name(); - } + if (nextToken().type != TOK_NAME) { + return nextToken().name(); + } - bool nextNameContainsEscape() const { - if (nextToken().type == TOK_YIELD) - return false; - MOZ_ASSERT(nextToken().type == TOK_NAME); - return nextToken().nameContainsEscape(); + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(nextToken().type)); + return reservedWordToPropertyName(nextToken().type); } bool isCurrentTokenAssignment() const { @@ -498,9 +498,6 @@ class MOZ_STACK_CLASS TokenStream {} }; - bool awaitIsKeyword = false; - friend class AutoAwaitIsKeyword; - uint32_t invalidTemplateEscapeOffset = 0; InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None; @@ -508,14 +505,12 @@ class MOZ_STACK_CLASS TokenStream typedef Token::Modifier Modifier; static constexpr Modifier None = Token::None; static constexpr Modifier Operand = Token::Operand; - static constexpr Modifier KeywordIsName = Token::KeywordIsName; static constexpr Modifier TemplateTail = Token::TemplateTail; typedef Token::ModifierException ModifierException; static constexpr ModifierException NoException = Token::NoException; static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand; static constexpr ModifierException OperandIsNone = Token::OperandIsNone; - static constexpr ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName; void addModifierException(ModifierException modifierException) { #ifdef DEBUG @@ -544,10 +539,6 @@ class MOZ_STACK_CLASS TokenStream MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP, "next token requires contextual specifier to be parsed unambiguously"); break; - case NoneIsKeywordIsName: - MOZ_ASSERT(next.modifier == KeywordIsName); - MOZ_ASSERT(next.type != TOK_NAME); - break; default: MOZ_CRASH("unexpected modifier exception"); } @@ -574,12 +565,6 @@ class MOZ_STACK_CLASS TokenStream return; } - if (lookaheadToken.modifierException == NoneIsKeywordIsName) { - // getToken() permissibly following getToken(KeywordIsName). - if (modifier == None && lookaheadToken.modifier == KeywordIsName) - return; - } - MOZ_ASSERT_UNREACHABLE("this token was previously looked up with a " "different modifier, potentially making " "tokenization non-deterministic"); @@ -714,36 +699,6 @@ class MOZ_STACK_CLASS TokenStream MOZ_ALWAYS_TRUE(matched); } - // Like matchToken(..., TOK_NAME) but further matching the name token only - // if it has the given characters, without containing escape sequences. - // If the name token has the given characters yet *does* contain an escape, - // a syntax error will be reported. - // - // This latter behavior makes this method unsuitable for use in any context - // where ASI might occur. In such places, an escaped "contextual keyword" - // on a new line is the start of an ExpressionStatement, not a continuation - // of a StatementListItem (or ImportDeclaration or ExportDeclaration, in - // modules). - MOZ_MUST_USE bool matchContextualKeyword(bool* matchedp, Handle keyword, - Modifier modifier = None) - { - TokenKind token; - if (!getToken(&token, modifier)) - return false; - if (token == TOK_NAME && currentToken().name() == keyword) { - if (currentToken().nameContainsEscape()) { - reportError(JSMSG_ESCAPED_KEYWORD); - return false; - } - - *matchedp = true; - } else { - *matchedp = false; - ungetToken(); - } - return true; - } - MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) { TokenKind tt; if (!peekToken(&tt)) @@ -809,19 +764,6 @@ class MOZ_STACK_CLASS TokenStream return sourceMapURL_.get(); } - // If |atom| is not a keyword in this version, return true with *ttp - // unchanged. - // - // If it is a reserved word in this version and strictness mode, and thus - // can't be present in correct code, report a SyntaxError and return false. - // - // If it is a keyword, like "if", return true with the keyword's TokenKind - // in *ttp. - MOZ_MUST_USE bool checkForKeyword(JSAtom* atom, TokenKind* ttp); - - // Same semantics as above, but for the provided keyword. - MOZ_MUST_USE bool checkForKeyword(const KeywordInfo* kw, TokenKind* ttp); - // This class maps a userbuf offset (which is 0-indexed) to a line number // (which is 1-indexed) and a column index (which is 0-indexed). class SourceCoords @@ -1103,25 +1045,6 @@ class MOZ_STACK_CLASS TokenStream StrictModeGetter* strictModeGetter; // used to test for strict mode }; -class MOZ_STACK_CLASS AutoAwaitIsKeyword -{ -private: - TokenStream* ts_; - bool oldAwaitIsKeyword_; - -public: - AutoAwaitIsKeyword(TokenStream* ts, bool awaitIsKeyword) { - ts_ = ts; - oldAwaitIsKeyword_ = ts_->awaitIsKeyword; - ts_->awaitIsKeyword = awaitIsKeyword; - } - - ~AutoAwaitIsKeyword() { - ts_->awaitIsKeyword = oldAwaitIsKeyword_; - ts_ = nullptr; - } -}; - extern const char* TokenKindToDesc(TokenKind tt); -- cgit v1.2.3 From fcdc8929f376207be3594f809adda3c65d487101 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 16:25:42 -0400 Subject: 1336783 - Part 2: Remove Parser::checkStrictBinding. --- js/src/frontend/Parser.cpp | 138 ++++++++++++++++++++++----------------------- js/src/frontend/Parser.h | 5 +- 2 files changed, 70 insertions(+), 73 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 9b057fb72..560dc5ec8 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -974,44 +974,6 @@ ParserBase::isValidStrictBinding(PropertyName* name) !IsStrictReservedWord(name); } -/* - * Check that it is permitted to introduce a binding for |name|. Use |pos| for - * reporting error locations. - */ -template -bool -Parser::checkStrictBinding(PropertyName* name, TokenPos pos) -{ - if (!pc->sc()->needStrictChecks()) - return true; - - if (name == context->names().arguments) - return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, "arguments"); - - if (name == context->names().eval) - return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, "eval"); - - if (name == context->names().let) { - errorAt(pos.begin, JSMSG_RESERVED_ID, "let"); - return false; - } - - if (name == context->names().static_) { - errorAt(pos.begin, JSMSG_RESERVED_ID, "static"); - return false; - } - - if (name == context->names().yield) { - errorAt(pos.begin, JSMSG_RESERVED_ID, "yield"); - return false; - } - - if (IsStrictReservedWord(name)) - return strictModeErrorAt(pos.begin, JSMSG_RESERVED_ID, ReservedWordToCharZ(name)); - - return true; -} - /* * Returns true if all parameter names are valid strict mode binding names and * no duplicate parameter names are present. @@ -1094,9 +1056,6 @@ Parser::notePositionalFormalParameter(Node fn, HandlePropertyName if (!paramNode) return false; - if (!checkStrictBinding(name, pos())) - return false; - handler.addFunctionFormalParameter(fn, paramNode); return true; } @@ -1357,9 +1316,6 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind if (pc->useAsmOrInsideUseAsm()) return true; - if (!checkStrictBinding(name, pos)) - return false; - switch (kind) { case DeclarationKind::Var: case DeclarationKind::BodyLevelFunction: @@ -3608,8 +3564,26 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) { RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName()); - if (!checkStrictBinding(propertyName, handler.getPosition(pn))) - return false; + // `await` cannot be checked at this point because of different context. + // It should already be checked before this point. + if (propertyName != context->names().await) { + YieldHandling nameYieldHandling; + if (kind == Expression) { + // Named lambda has binding inside it. + nameYieldHandling = bodyYieldHandling; + } else { + // Otherwise YieldHandling cannot be checked at this point + // because of different context. + // It should already be checked before this point. + nameYieldHandling = YieldIsName; + } + + if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin, + nameYieldHandling)) + { + return false; + } + } } if (bodyType == StatementListBody) { @@ -4178,6 +4152,9 @@ Parser::checkDestructuringName(ParseNode* expr, Maybename()); + // `yield` is already checked, so pass YieldIsName to skip that check. + if (!checkBindingIdentifier(name, expr->pn_pos.begin, YieldIsName)) + return false; return noteDeclaredName(name, *maybeDecl, expr->pn_pos); } @@ -8120,7 +8097,7 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) // FIXME: Destructuring binding (bug 980828). MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME); - RootedPropertyName name(context, tokenStream.currentName()); + RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword)); if (name == context->names().let) { error(JSMSG_LET_COMP_BINDING); return null(); @@ -8644,12 +8621,16 @@ Parser::checkLabelOrIdentifierReference(PropertyName* ident, { if (ident == context->names().yield) { if (yieldHandling == YieldIsKeyword || - pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) { errorAt(offset, JSMSG_RESERVED_ID, "yield"); return nullptr; } + if (pc->sc()->needStrictChecks()) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield")) + return nullptr; + } + return ident; } @@ -8671,20 +8652,49 @@ Parser::checkLabelOrIdentifierReference(PropertyName* ident, return nullptr; } - if (pc->sc()->strict()) { + if (pc->sc()->needStrictChecks()) { if (IsStrictReservedWord(ident)) { - errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)); - return nullptr; + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident))) + return nullptr; + return ident; } if (ident == context->names().let) { - errorAt(offset, JSMSG_RESERVED_ID, "let"); - return nullptr; + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let")) + return nullptr; + return ident; } if (ident == context->names().static_) { - errorAt(offset, JSMSG_RESERVED_ID, "static"); - return nullptr; + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static")) + return nullptr; + return ident; + } + } + + return ident; +} + +template +PropertyName* +Parser::checkBindingIdentifier(PropertyName* ident, + uint32_t offset, + YieldHandling yieldHandling) +{ + if (!checkLabelOrIdentifierReference(ident, offset, yieldHandling)) + return nullptr; + + if (pc->sc()->needStrictChecks()) { + if (ident == context->names().arguments) { + if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments")) + return nullptr; + return ident; + } + + if (ident == context->names().eval) { + if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "eval")) + return nullptr; + return ident; } } @@ -8709,23 +8719,7 @@ template PropertyName* Parser::bindingIdentifier(YieldHandling yieldHandling) { - PropertyName* ident = labelOrIdentifierReference(yieldHandling); - if (!ident) - return nullptr; - - if (pc->sc()->strict()) { - if (ident == context->names().arguments) { - error(JSMSG_BAD_STRICT_ASSIGN, "arguments"); - return nullptr; - } - - if (ident == context->names().eval) { - error(JSMSG_BAD_STRICT_ASSIGN, "eval"); - return nullptr; - } - } - - return ident; + return checkBindingIdentifier(tokenStream.currentName(), pos().begin, yieldHandling); } template diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 9ec862d92..d53a64eeb 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1335,6 +1335,10 @@ class Parser final : public ParserBase, private JS::AutoGCRooter uint32_t offset, YieldHandling yieldHandling); + PropertyName* checkBindingIdentifier(PropertyName* ident, + uint32_t offset, + YieldHandling yieldHandling); + PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling); PropertyName* labelIdentifier(YieldHandling yieldHandling) { @@ -1404,7 +1408,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter private: bool checkIncDecOperand(Node operand, uint32_t operandOffset); bool checkStrictAssignment(Node lhs); - bool checkStrictBinding(PropertyName* name, TokenPos pos); bool hasValidSimpleStrictParameterNames(); -- cgit v1.2.3 From dd0057fbf08d56bbcb3c61900ae2cfc8d359080d Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 16:29:42 -0400 Subject: 1336783 - Part 3 - Use simpler macro to avoid internal compiler error on SM-tc(H). --- js/src/frontend/Parser.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 560dc5ec8..82057f62e 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -63,24 +63,27 @@ using BindingIter = ParseContext::Scope::BindingIter; using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr; // Read a token. Report an error and return null() if that token doesn't match -// to the given func's condition. -#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \ +// to the condition. Do not use MUST_MATCH_TOKEN_INTERNAL directly. +#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorNumber) \ JS_BEGIN_MACRO \ TokenKind token; \ if (!tokenStream.getToken(&token, modifier)) \ return null(); \ - if (!(func)(token)) { \ + if (!(cond)) { \ error(errorNumber); \ return null(); \ } \ JS_END_MACRO #define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \ - MUST_MATCH_TOKEN_FUNC_MOD([](TokenKind tok) { return tok == tt; }, modifier, errorNumber) + MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorNumber) #define MUST_MATCH_TOKEN(tt, errorNumber) \ MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber) +#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \ + MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, errorNumber) + #define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \ MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber) -- cgit v1.2.3 From 0f5f3c30490741c3053627de4fd5a27964c9953c Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 16:36:59 -0400 Subject: 1336783 - followup: Fix rooting. --- js/src/frontend/Parser.cpp | 58 +++++++++++++++++++++++++--------------------- js/src/frontend/Parser.h | 10 ++++---- 2 files changed, 37 insertions(+), 31 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 82057f62e..322f428e5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -8617,8 +8617,8 @@ Parser::newName(PropertyName* name, TokenPos pos) } template -PropertyName* -Parser::checkLabelOrIdentifierReference(PropertyName* ident, +bool +Parser::checkLabelOrIdentifierReference(HandlePropertyName ident, uint32_t offset, YieldHandling yieldHandling) { @@ -8627,81 +8627,81 @@ Parser::checkLabelOrIdentifierReference(PropertyName* ident, versionNumber() >= JSVERSION_1_7) { errorAt(offset, JSMSG_RESERVED_ID, "yield"); - return nullptr; + return false; } if (pc->sc()->needStrictChecks()) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield")) - return nullptr; + return false; } - return ident; + return true; } if (ident == context->names().await) { if (awaitIsKeyword()) { errorAt(offset, JSMSG_RESERVED_ID, "await"); - return nullptr; + return false; } - return ident; + return true; } if (IsKeyword(ident) || IsReservedWordLiteral(ident)) { errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(ident)); - return nullptr; + return false; } if (IsFutureReservedWord(ident)) { errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)); - return nullptr; + return false; } if (pc->sc()->needStrictChecks()) { if (IsStrictReservedWord(ident)) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident))) - return nullptr; - return ident; + return false; + return true; } if (ident == context->names().let) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let")) - return nullptr; - return ident; + return false; + return true; } if (ident == context->names().static_) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static")) - return nullptr; - return ident; + return false; + return true; } } - return ident; + return true; } template -PropertyName* -Parser::checkBindingIdentifier(PropertyName* ident, +bool +Parser::checkBindingIdentifier(HandlePropertyName ident, uint32_t offset, YieldHandling yieldHandling) { if (!checkLabelOrIdentifierReference(ident, offset, yieldHandling)) - return nullptr; + return false; if (pc->sc()->needStrictChecks()) { if (ident == context->names().arguments) { if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments")) - return nullptr; - return ident; + return false; + return true; } if (ident == context->names().eval) { if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "eval")) - return nullptr; - return ident; + return false; + return true; } } - return ident; + return true; } template @@ -8715,14 +8715,20 @@ Parser::labelOrIdentifierReference(YieldHandling yieldHandling) // // Use PropertyName* instead of TokenKind to reflect the normalization. - return checkLabelOrIdentifierReference(tokenStream.currentName(), pos().begin, yieldHandling); + RootedPropertyName ident(context, tokenStream.currentName()); + if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling)) + return nullptr; + return ident; } template PropertyName* Parser::bindingIdentifier(YieldHandling yieldHandling) { - return checkBindingIdentifier(tokenStream.currentName(), pos().begin, yieldHandling); + RootedPropertyName ident(context, tokenStream.currentName()); + if (!checkBindingIdentifier(ident, pos().begin, yieldHandling)) + return nullptr; + return ident; } template diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index d53a64eeb..4eb0f5a39 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1331,14 +1331,14 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling); - PropertyName* checkLabelOrIdentifierReference(PropertyName* ident, - uint32_t offset, - YieldHandling yieldHandling); - - PropertyName* checkBindingIdentifier(PropertyName* ident, + bool checkLabelOrIdentifierReference(HandlePropertyName ident, uint32_t offset, YieldHandling yieldHandling); + bool checkBindingIdentifier(HandlePropertyName ident, + uint32_t offset, + YieldHandling yieldHandling); + PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling); PropertyName* labelIdentifier(YieldHandling yieldHandling) { -- cgit v1.2.3 From c1ba97eeae3171fb96283583c33f0238cedacab6 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 17:20:09 -0400 Subject: 1340089 - Check the binding name in comprehensionFor. --- js/src/frontend/Parser.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 322f428e5..fc4b0e965 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -8101,6 +8101,8 @@ Parser::comprehensionFor(GeneratorKind comprehensionKind) MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME); RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword)); + if (!name) + return null(); if (name == context->names().let) { error(JSMSG_LET_COMP_BINDING); return null(); -- cgit v1.2.3 From 57a8b65fc0c4bd12149855d972aea05828e4d6e7 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 22:30:59 -0400 Subject: 1339963 - Part 1: Split Parser::exportDeclaration. --- js/src/frontend/Parser.cpp | 659 ++++++++++++++++++++++++----------- js/src/frontend/Parser.h | 23 +- js/src/frontend/SyntaxParseHandler.h | 14 + 3 files changed, 486 insertions(+), 210 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index fc4b0e965..16d4a377a 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4692,8 +4692,10 @@ Parser::declarationList(YieldHandling yieldHandling, template typename ParseHandler::Node -Parser::lexicalDeclaration(YieldHandling yieldHandling, bool isConst) +Parser::lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind) { + MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); + /* * Parse body-level lets without a new block object. ES6 specs * that an execution environment's initial lexical environment @@ -4705,7 +4707,8 @@ Parser::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); + Node decl = declarationList(yieldHandling, + kind == DeclarationKind::Const ? PNK_CONST : PNK_LET); if (!decl || !matchOrInsertSemicolonAfterExpression()) return null(); @@ -4994,279 +4997,517 @@ Parser::checkExportedNamesForDeclaration(Node node) } template<> -ParseNode* -Parser::exportDeclaration() +bool +Parser::checkExportedNameForClause(ParseNode* node) { - MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); + return checkExportedName(node->pn_atom); +} - if (!pc->atModuleLevel()) { - error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); +template<> +bool +Parser::checkExportedNameForClause(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser::checkExportedNameForFunction(ParseNode* node) +{ + return checkExportedName(node->pn_funbox->function()->explicitName()); +} + +template<> +bool +Parser::checkExportedNameForFunction(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser::checkExportedNameForClass(ParseNode* node) +{ + const ClassNode& cls = node->as(); + MOZ_ASSERT(cls.names()); + return checkExportedName(cls.names()->innerBinding()->pn_atom); +} + +template<> +bool +Parser::checkExportedNameForClass(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser::processExport(ParseNode* node) +{ + return pc->sc()->asModuleContext()->builder.processExport(node); +} + +template<> +bool +Parser::processExport(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template<> +bool +Parser::processExportFrom(ParseNode* node) +{ + return pc->sc()->asModuleContext()->builder.processExportFrom(node); +} + +template<> +bool +Parser::processExportFrom(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + +template +typename ParseHandler::Node +Parser::exportFrom(uint32_t begin, Node specList) +{ + if (!abortIfSyntaxParser()) return null(); - } - uint32_t begin = pos().begin; + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FROM)); - Node kid; - TokenKind tt; - if (!tokenStream.getToken(&tt)) + if (!abortIfSyntaxParser()) return null(); - switch (tt) { - case TOK_LC: { - kid = handler.newList(PNK_EXPORT_SPEC_LIST); - if (!kid) - return null(); - while (true) { - // Handle the forms |export {}| and |export { ..., }| (where ... - // is non empty), by escaping the loop early if the next token - // is }. - if (!tokenStream.getToken(&tt)) - return null(); + MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); - if (tt == TOK_RC) - break; + Node moduleSpec = stringLiteral(); + if (!moduleSpec) + return null(); - if (!TokenKindIsPossibleIdentifierName(tt)) { - error(JSMSG_NO_BINDING_NAME); - return null(); - } + if (!matchOrInsertSemicolonAfterNonExpression()) + return null(); - Node bindingName = newName(tokenStream.currentName()); - if (!bindingName) - return null(); + Node node = handler.newExportFromDeclaration(begin, specList, moduleSpec); + if (!node) + return null(); - bool foundAs; - if (!tokenStream.matchToken(&foundAs, TOK_AS)) - return null(); - if (foundAs) - MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME); + if (!processExportFrom(node)) + return null(); - Node exportName = newName(tokenStream.currentName()); - if (!exportName) - return null(); + return node; +} - if (!checkExportedName(exportName->pn_atom)) - return null(); +template +typename ParseHandler::Node +Parser::exportBatch(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); - Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); - if (!exportSpec) - return null(); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_MUL)); - handler.addList(kid, exportSpec); + Node kid = handler.newList(PNK_EXPORT_SPEC_LIST); + if (!kid) + return null(); - TokenKind next; - if (!tokenStream.getToken(&next)) - return null(); + // Handle the form |export *| by adding a special export batch + // specifier to the list. + Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); + if (!exportSpec) + return null(); - if (next == TOK_RC) - break; + handler.addList(kid, exportSpec); - if (next != TOK_COMMA) { - error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST); - return null(); - } - } + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + if (tt != TOK_FROM) { + error(JSMSG_FROM_AFTER_EXPORT_STAR); + return null(); + } - // Careful! If |from| follows, even on a new line, it must start a - // FromClause: - // - // export { x } - // from "foo"; // a single ExportDeclaration - // - // But if it doesn't, we might have an ASI opportunity in Operand - // context: - // - // export { x } // ExportDeclaration, terminated by ASI - // fro\u006D // ExpressionStatement, the name "from" - // - // In that case let matchOrInsertSemicolonAfterNonExpression sort out - // ASI or any necessary error. - bool matched; - if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand)) - return null(); + return exportFrom(begin, kid); +} - if (matched) { - MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); +template +typename ParseHandler::Node +Parser::exportClause(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); - Node moduleSpec = stringLiteral(); - if (!moduleSpec) - return null(); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); - if (!matchOrInsertSemicolonAfterNonExpression()) - return null(); + Node kid = handler.newList(PNK_EXPORT_SPEC_LIST); + if (!kid) + return null(); - ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec); - if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node)) - return null(); + TokenKind tt; + while (true) { + // Handle the forms |export {}| and |export { ..., }| (where ... is non + // empty), by escaping the loop early if the next token is }. + if (!tokenStream.getToken(&tt)) + return null(); + + if (tt == TOK_RC) + break; - return node; + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_NO_BINDING_NAME); + return null(); } - if (!matchOrInsertSemicolonAfterNonExpression()) + Node bindingName = newName(tokenStream.currentName()); + if (!bindingName) return null(); - break; - } - case TOK_MUL: { - kid = handler.newList(PNK_EXPORT_SPEC_LIST); - if (!kid) + bool foundAs; + if (!tokenStream.matchToken(&foundAs, TOK_AS)) + return null(); + if (foundAs) + MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME); + + Node exportName = newName(tokenStream.currentName()); + if (!exportName) return null(); - // Handle the form |export *| by adding a special export batch - // specifier to the list. - Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos()); + if (!checkExportedNameForClause(exportName)) + return null(); + + Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName); if (!exportSpec) return null(); handler.addList(kid, exportSpec); - if (!tokenStream.getToken(&tt)) + TokenKind next; + if (!tokenStream.getToken(&next)) return null(); - if (tt != TOK_FROM) { - error(JSMSG_FROM_AFTER_EXPORT_STAR); + + if (next == TOK_RC) + break; + + if (next != TOK_COMMA) { + error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST); return null(); } + } - MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); + // Careful! If |from| follows, even on a new line, it must start a + // FromClause: + // + // export { x } + // from "foo"; // a single ExportDeclaration + // + // But if it doesn't, we might have an ASI opportunity in Operand context: + // + // export { x } // ExportDeclaration, terminated by ASI + // fro\u006D // ExpressionStatement, the name "from" + // + // In that case let matchOrInsertSemicolonAfterNonExpression sort out ASI + // or any necessary error. + bool matched; + if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand)) + return null(); - Node moduleSpec = stringLiteral(); - if (!moduleSpec) - return null(); + if (matched) + return exportFrom(begin, kid); - if (!matchOrInsertSemicolonAfterNonExpression()) - return null(); + if (!matchOrInsertSemicolonAfterNonExpression()) + return null(); - ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec); - if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node)) - return null(); + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); - return node; + if (!processExport(node)) + return null(); - } + return node; +} + +template +typename ParseHandler::Node +Parser::exportVariableStatement(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_VAR)); + + Node kid = declarationList(YieldIsName, PNK_VAR); + if (!kid) + return null(); + if (!matchOrInsertSemicolonAfterExpression()) + return null(); + if (!checkExportedNamesForDeclaration(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportFunctionDeclaration(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + + Node kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired); + if (!kid) + return null(); + + if (!checkExportedNameForFunction(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportClassDeclaration(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + + Node kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired); + if (!kid) + return null(); + + if (!checkExportedNameForClass(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportLexicalDeclaration(uint32_t begin, DeclarationKind kind) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); + MOZ_ASSERT_IF(kind == DeclarationKind::Const, tokenStream.isCurrentTokenType(TOK_CONST)); + MOZ_ASSERT_IF(kind == DeclarationKind::Let, tokenStream.isCurrentTokenType(TOK_LET)); + + Node kid = lexicalDeclaration(YieldIsName, kind); + if (!kid) + return null(); + if (!checkExportedNamesForDeclaration(kid)) + return null(); + + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportDefaultFunctionDeclaration(uint32_t begin, + FunctionAsyncKind asyncKind + /* = SyncFunction */) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); + + Node kid = functionStmt(pos().begin, YieldIsKeyword, AllowDefaultName, asyncKind); + if (!kid) + return null(); + + Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportDefaultClassDeclaration(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + + Node kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName); + if (!kid) + return null(); + + Node node = handler.newExportDefaultDeclaration(kid, null(), TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportDefaultAssignExpr(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + RootedPropertyName name(context, context->names().starDefaultStar); + Node nameNode = newName(name); + if (!nameNode) + return null(); + if (!noteDeclaredName(name, DeclarationKind::Const, pos())) + return null(); + + Node kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); + if (!kid) + return null(); + if (!matchOrInsertSemicolonAfterExpression()) + return null(); + + Node node = handler.newExportDefaultDeclaration(kid, nameNode, TokenPos(begin, pos().end)); + if (!node) + return null(); + + if (!processExport(node)) + return null(); + + return node; +} + +template +typename ParseHandler::Node +Parser::exportDefault(uint32_t begin) +{ + if (!abortIfSyntaxParser()) + return null(); + + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_DEFAULT)); + + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + if (!checkExportedName(context->names().default_)) + return null(); + switch (tt) { case TOK_FUNCTION: - kid = functionStmt(pos().begin, YieldIsKeyword, NameRequired); - if (!kid) - return null(); + return exportDefaultFunctionDeclaration(begin); - if (!checkExportedName(kid->pn_funbox->function()->explicitName())) + case TOK_ASYNC: { + TokenKind nextSameLine = TOK_EOF; + if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); - break; - case TOK_CLASS: { - kid = classDefinition(YieldIsKeyword, ClassStatement, NameRequired); - if (!kid) - return null(); + if (nextSameLine == TOK_FUNCTION) { + tokenStream.consumeKnownToken(TOK_FUNCTION); + return exportDefaultFunctionDeclaration(begin, AsyncFunction); + } - const ClassNode& cls = kid->as(); - MOZ_ASSERT(cls.names()); - if (!checkExportedName(cls.names()->innerBinding()->pn_atom)) - return null(); - break; + tokenStream.ungetToken(); + return exportDefaultAssignExpr(begin); } - case TOK_VAR: - kid = declarationList(YieldIsName, PNK_VAR); - if (!kid) - return null(); - if (!matchOrInsertSemicolonAfterExpression()) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; + case TOK_CLASS: + return exportDefaultClassDeclaration(begin); - case TOK_DEFAULT: { - if (!tokenStream.getToken(&tt, TokenStream::Operand)) - return null(); + default: + tokenStream.ungetToken(); + return exportDefaultAssignExpr(begin); + } +} - if (!checkExportedName(context->names().default_)) - return null(); +template +typename ParseHandler::Node +Parser::exportDeclaration() +{ + if (!abortIfSyntaxParser()) + return null(); - ParseNode* nameNode = nullptr; - switch (tt) { - case TOK_FUNCTION: - kid = functionStmt(pos().begin, YieldIsKeyword, AllowDefaultName); - if (!kid) - return null(); - break; - case TOK_CLASS: - kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName); - if (!kid) - return null(); - break; - default: { - if (tt == TOK_ASYNC) { - TokenKind nextSameLine = TOK_EOF; - if (!tokenStream.peekTokenSameLine(&nextSameLine)) - return null(); + MOZ_ASSERT(tokenStream.currentToken().type == TOK_EXPORT); - if (nextSameLine == TOK_FUNCTION) { - tokenStream.consumeKnownToken(nextSameLine); - kid = functionStmt(pos().begin, YieldIsName, AllowDefaultName, AsyncFunction); - if (!kid) - return null(); - break; - } - } + if (!pc->atModuleLevel()) { + error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); + return null(); + } - tokenStream.ungetToken(); - RootedPropertyName name(context, context->names().starDefaultStar); - nameNode = newName(name); - if (!nameNode) - return null(); - if (!noteDeclaredName(name, DeclarationKind::Const, pos())) - return null(); - kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); - if (!kid) - return null(); - if (!matchOrInsertSemicolonAfterExpression()) - return null(); - break; - } - } + uint32_t begin = pos().begin; - ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode, - TokenPos(begin, pos().end)); - if (!node || !pc->sc()->asModuleContext()->builder.processExport(node)) - return null(); + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + switch (tt) { + case TOK_MUL: + return exportBatch(begin); - return node; - } + case TOK_LC: + return exportClause(begin); + + case TOK_VAR: + return exportVariableStatement(begin); + + case TOK_FUNCTION: + return exportFunctionDeclaration(begin); + + case TOK_CLASS: + return exportClassDeclaration(begin); case TOK_CONST: - kid = lexicalDeclaration(YieldIsName, /* isConst = */ true); - if (!kid) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; + return exportLexicalDeclaration(begin, DeclarationKind::Const); case TOK_LET: - kid = lexicalDeclaration(YieldIsName, /* isConst = */ false); - if (!kid) - return null(); - if (!checkExportedNamesForDeclaration(kid)) - return null(); - break; + return exportLexicalDeclaration(begin, DeclarationKind::Let); + + case TOK_DEFAULT: + return exportDefault(begin); default: error(JSMSG_DECLARATION_AFTER_EXPORT); return null(); } - - ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); - if (!node || !pc->sc()->asModuleContext()->builder.processExport(node)) - return null(); - - return node; -} - -template<> -SyntaxParseHandler::Node -Parser::exportDeclaration() -{ - JS_ALWAYS_FALSE(abortIfSyntaxParser()); - return SyntaxParseHandler::NodeFailure; } template @@ -7132,7 +7373,7 @@ Parser::statementListItem(YieldHandling yieldHandling, return null(); if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next, yieldHandling)) - return lexicalDeclaration(yieldHandling, /* isConst = */ false); + return lexicalDeclaration(yieldHandling, DeclarationKind::Let); if (tt == TOK_ASYNC) { TokenKind nextSameLine = TOK_EOF; @@ -7228,7 +7469,7 @@ Parser::statementListItem(YieldHandling yieldHandling, case TOK_CONST: // [In] is the default behavior, because for-loops specially parse // their heads to handle |in| in this situation. - return lexicalDeclaration(yieldHandling, /* isConst = */ true); + return lexicalDeclaration(yieldHandling, DeclarationKind::Const); // ImportDeclaration (only inside modules) case TOK_IMPORT: diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 4eb0f5a39..e322a4b26 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1193,9 +1193,26 @@ class Parser final : public ParserBase, private JS::AutoGCRooter // continues a LexicalDeclaration. bool nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling); - Node lexicalDeclaration(YieldHandling yieldHandling, bool isConst); + Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind); Node importDeclaration(); + + bool processExport(Node node); + bool processExportFrom(Node node); + + Node exportFrom(uint32_t begin, Node specList); + Node exportBatch(uint32_t begin); + Node exportClause(uint32_t begin); + Node exportFunctionDeclaration(uint32_t begin); + Node exportVariableStatement(uint32_t begin); + Node exportClassDeclaration(uint32_t begin); + Node exportLexicalDeclaration(uint32_t begin, DeclarationKind kind); + Node exportDefaultFunctionDeclaration(uint32_t begin, + FunctionAsyncKind asyncKind = SyncFunction); + Node exportDefaultClassDeclaration(uint32_t begin); + Node exportDefaultAssignExpr(uint32_t begin); + Node exportDefault(uint32_t begin); + Node exportDeclaration(); Node expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked); @@ -1327,6 +1344,10 @@ class Parser final : public ParserBase, private JS::AutoGCRooter bool checkExportedName(JSAtom* exportName); bool checkExportedNamesForDeclaration(Node node); + bool checkExportedNameForClause(Node node); + bool checkExportedNameForFunction(Node node); + bool checkExportedNameForClass(Node node); + enum ClassContext { ClassStatement, ClassExpression }; Node classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index aa85a631e..f492bab66 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -241,6 +241,10 @@ class SyntaxParseHandler return NodeUnparenthesizedUnary; } + Node newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) { + return NodeGeneric; + } + Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) { return NodeUnparenthesizedUnary; } @@ -308,6 +312,16 @@ class SyntaxParseHandler MOZ_MUST_USE bool prependInitialYield(Node stmtList, Node gen) { return true; } Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; } + Node newExportDeclaration(Node kid, const TokenPos& pos) { + return NodeGeneric; + } + Node newExportFromDeclaration(uint32_t begin, Node exportSpecSet, Node moduleSpec) { + return NodeGeneric; + } + Node newExportDefaultDeclaration(Node kid, Node maybeBinding, const TokenPos& pos) { + return NodeGeneric; + } + Node newSetThis(Node thisName, Node value) { return value; } Node newExprStatement(Node expr, uint32_t end) { -- cgit v1.2.3 From d2aa017a88a4b918ded2159cd202ce6c7c7218d0 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 22:32:35 -0400 Subject: 1339963 - Part 2: Use MUST_MATCH_TOKEN for contextual keyword where it can be used. --- js/src/frontend/Parser.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 16d4a377a..f382ec5e9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4915,13 +4915,7 @@ Parser::importDeclaration() return null(); } - if (!tokenStream.getToken(&tt)) - return null(); - - if (tt != TOK_FROM) { - error(JSMSG_FROM_AFTER_IMPORT_CLAUSE); - return null(); - } + MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_IMPORT_CLAUSE); MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM); } @@ -5125,13 +5119,7 @@ Parser::exportBatch(uint32_t begin) handler.addList(kid, exportSpec); - TokenKind tt; - if (!tokenStream.getToken(&tt)) - return null(); - if (tt != TOK_FROM) { - error(JSMSG_FROM_AFTER_EXPORT_STAR); - return null(); - } + MUST_MATCH_TOKEN(TOK_FROM, JSMSG_FROM_AFTER_EXPORT_STAR); return exportFrom(begin, kid); } -- cgit v1.2.3 From 64fd7ae4546424c42a9e2022e05b83379411109a Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 22:35:15 -0400 Subject: 1339963 - Part 3: Check IdentifierName in ExportClause without from. --- js/src/frontend/Parser.cpp | 28 ++++++++++++++++++++++++++++ js/src/frontend/Parser.h | 5 +++++ 2 files changed, 33 insertions(+) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index f382ec5e9..ffece5721 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -5124,6 +5124,31 @@ Parser::exportBatch(uint32_t begin) return exportFrom(begin, kid); } +template<> +bool +Parser::checkLocalExportNames(ParseNode* node) +{ + // ES 2017 draft 15.2.3.1. + for (ParseNode* next = node->pn_head; next; next = next->pn_next) { + ParseNode* name = next->pn_left; + MOZ_ASSERT(name->isKind(PNK_NAME)); + + RootedPropertyName ident(context, name->pn_atom->asPropertyName()); + if (!checkLocalExportName(ident, name->pn_pos.begin)) + return false; + } + + return true; +} + +template<> +bool +Parser::checkLocalExportNames(Node node) +{ + MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); + return false; +} + template typename ParseHandler::Node Parser::exportClause(uint32_t begin) @@ -5211,6 +5236,9 @@ Parser::exportClause(uint32_t begin) if (!matchOrInsertSemicolonAfterNonExpression()) return null(); + if (!checkLocalExportNames(kid)) + return null(); + Node node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end)); if (!node) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index e322a4b26..824dddb58 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1202,6 +1202,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node exportFrom(uint32_t begin, Node specList); Node exportBatch(uint32_t begin); + bool checkLocalExportNames(Node node); Node exportClause(uint32_t begin); Node exportFunctionDeclaration(uint32_t begin); Node exportVariableStatement(uint32_t begin); @@ -1356,6 +1357,10 @@ class Parser final : public ParserBase, private JS::AutoGCRooter uint32_t offset, YieldHandling yieldHandling); + bool checkLocalExportName(HandlePropertyName ident, uint32_t offset) { + return checkLabelOrIdentifierReference(ident, offset, YieldIsName); + } + bool checkBindingIdentifier(HandlePropertyName ident, uint32_t offset, YieldHandling yieldHandling); -- cgit v1.2.3 From 8a4aeb6f58649af6e0a6d1df30b133510cbb2187 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 22:44:25 -0400 Subject: 1342273 - Improve frontend tracelogging. [PARTIAL] Only implemented const to rvalue changes, no tracelogging --- js/src/frontend/BytecodeEmitter.cpp | 32 +++++++++++++++++--------------- js/src/frontend/BytecodeEmitter.h | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 7b8b41727..c15d67b7c 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3539,9 +3539,10 @@ BytecodeEmitter::needsImplicitThis() bool BytecodeEmitter::maybeSetDisplayURL() { - if (tokenStream()->hasDisplayURL()) { - if (!parser->ss->setDisplayURL(cx, tokenStream()->displayURL())) + if (tokenStream().hasDisplayURL()) { + if (!parser->ss->setDisplayURL(cx, tokenStream().displayURL())) { return false; + } } return true; } @@ -3549,10 +3550,11 @@ BytecodeEmitter::maybeSetDisplayURL() bool BytecodeEmitter::maybeSetSourceMap() { - if (tokenStream()->hasSourceMapURL()) { + if (tokenStream().hasSourceMapURL()) { MOZ_ASSERT(!parser->ss->hasSourceMapURL()); - if (!parser->ss->setSourceMapURL(cx, tokenStream()->sourceMapURL())) + if (!parser->ss->setSourceMapURL(cx, tokenStream().sourceMapURL())) { return false; + } } /* @@ -3591,21 +3593,21 @@ BytecodeEmitter::tellDebuggerAboutCompiledScript(ExclusiveContext* cx) } } -inline TokenStream* +inline TokenStream& BytecodeEmitter::tokenStream() { - return &parser->tokenStream; + return parser->tokenStream; } bool BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) { - TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; + TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR, - errorNumber, args); + bool result = tokenStream().reportCompileErrorNumberVA(nullptr, pos.begin, JSREPORT_ERROR, + errorNumber, args); va_end(args); return result; } @@ -3613,12 +3615,12 @@ BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...) bool BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...) { - TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; + TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportExtraWarningErrorNumberVA(nullptr, pos.begin, - errorNumber, args); + bool result = tokenStream().reportExtraWarningErrorNumberVA(nullptr, pos.begin, + errorNumber, args); va_end(args); return result; } @@ -3626,12 +3628,12 @@ BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...) bool BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...) { - TokenPos pos = pn ? pn->pn_pos : tokenStream()->currentToken().pos; + TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos; va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(), - errorNumber, args); + bool result = tokenStream().reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(), + errorNumber, args); va_end(args); return result; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 29050c846..4e5209593 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -356,7 +356,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool maybeSetSourceMap(); void tellDebuggerAboutCompiledScript(ExclusiveContext* cx); - inline TokenStream* tokenStream(); + inline TokenStream& tokenStream(); BytecodeVector& code() const { return current->code; } jsbytecode* code(ptrdiff_t offset) const { return current->code.begin() + offset; } -- cgit v1.2.3 From dab8877c10fcc9dd5d86e6fd3d1924e474076726 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 9 Jun 2019 23:03:08 -0400 Subject: 104442 - Part 1: Report the position and the kind of previous declaration for redeclaration error. --- js/src/frontend/NameAnalysisTypes.h | 12 +++- js/src/frontend/Parser.cpp | 106 +++++++++++++++++++++++++++--------- js/src/frontend/Parser.h | 16 +++--- 3 files changed, 99 insertions(+), 35 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h index d39e177fb..b94ca6cc0 100644 --- a/js/src/frontend/NameAnalysisTypes.h +++ b/js/src/frontend/NameAnalysisTypes.h @@ -124,6 +124,7 @@ DeclarationKindIsLexical(DeclarationKind kind) // Used in Parser to track declared names. class DeclaredNameInfo { + uint32_t pos_; DeclarationKind kind_; // If the declared name is a binding, whether the binding is closed @@ -132,8 +133,9 @@ class DeclaredNameInfo bool closedOver_; public: - explicit DeclaredNameInfo(DeclarationKind kind) - : kind_(kind), + explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos) + : pos_(pos), + kind_(kind), closedOver_(false) { } @@ -144,6 +146,12 @@ class DeclaredNameInfo return kind_; } + static const uint32_t npos = uint32_t(-1); + + uint32_t pos() const { + return pos_; + } + void alterKind(DeclarationKind kind) { kind_ = kind; } diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index ffece5721..c96e8c065 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -19,6 +19,8 @@ #include "frontend/Parser.h" +#include "mozilla/Sprintf.h" + #include #include "jsapi.h" @@ -200,11 +202,12 @@ ParseContext::Scope::addCatchParameters(ParseContext* pc, Scope& catchParamScope for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) { DeclarationKind kind = r.front().value()->kind(); + uint32_t pos = r.front().value()->pos(); MOZ_ASSERT(DeclarationKindIsCatchParameter(kind)); JSAtom* name = r.front().key(); AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name); MOZ_ASSERT(!p); - if (!addDeclaredName(pc, p, name, kind)) + if (!addDeclaredName(pc, p, name, kind, pos)) return false; } @@ -343,7 +346,8 @@ ParseContext::init() namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName()); MOZ_ASSERT(!p); if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(), - DeclarationKind::Const)) + DeclarationKind::Const, + DeclaredNameInfo::npos)) { return false; } @@ -1001,13 +1005,42 @@ Parser::hasValidSimpleStrictParameterNames() template void -Parser::reportRedeclaration(HandlePropertyName name, DeclarationKind kind, - TokenPos pos) +Parser::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, + TokenPos pos, uint32_t prevPos) { JSAutoByteString bytes; if (!AtomToPrintableString(context, name, &bytes)) return; - errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(kind), bytes.ptr()); + + if (prevPos == DeclaredNameInfo::npos) { + errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr()); + return; + } + + auto notes = MakeUnique(); + if (!notes) + return; + + uint32_t line, column; + tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column); + + const size_t MaxWidth = sizeof("4294967295"); + char columnNumber[MaxWidth]; + SprintfLiteral(columnNumber, "%" PRIu32, column); + char lineNumber[MaxWidth]; + SprintfLiteral(lineNumber, "%" PRIu32, line); + + if (!notes->addNoteLatin1(pc->sc()->context, + getFilename(), line, column, + GetErrorMessage, nullptr, + JSMSG_REDECLARED_PREV, + lineNumber, columnNumber)) + { + return; + } + + errorWithNotesAt(Move(notes), pos.begin, JSMSG_REDECLARED_VAR, + DeclarationKindString(prevKind), bytes.ptr()); } // notePositionalFormalParameter is called for both the arguments of a regular @@ -1022,6 +1055,7 @@ Parser::reportRedeclaration(HandlePropertyName name, DeclarationKi template bool Parser::notePositionalFormalParameter(Node fn, HandlePropertyName name, + uint32_t beginPos, bool disallowDuplicateParams, bool* duplicatedParam) { @@ -1046,7 +1080,7 @@ Parser::notePositionalFormalParameter(Node fn, HandlePropertyName *duplicatedParam = true; } else { DeclarationKind kind = DeclarationKind::PositionalFormalParameter; - if (!pc->functionScope().addDeclaredName(pc, p, name, kind)) + if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos)) return false; } @@ -1150,8 +1184,8 @@ DeclarationKindIsParameter(DeclarationKind kind) template bool -Parser::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, - Maybe* redeclaredKind) +Parser::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos, + Maybe* redeclaredKind, uint32_t* prevPos) { MOZ_ASSERT(DeclarationKindIsVar(kind)); @@ -1224,23 +1258,29 @@ Parser::tryDeclareVar(HandlePropertyName name, DeclarationKind kin if (!annexB35Allowance && !annexB33Allowance) { *redeclaredKind = Some(declaredKind); + *prevPos = p->value()->pos(); return true; } } else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) { MOZ_ASSERT(DeclarationKindIsParameter(declaredKind)); // Annex B.3.3.1 disallows redeclaring parameter names. + // We don't need to set *prevPos here since this case is not + // an error. *redeclaredKind = Some(declaredKind); return true; } } else { - if (!scope->addDeclaredName(pc, p, name, kind)) + if (!scope->addDeclaredName(pc, p, name, kind, beginPos)) return false; } } - if (!pc->sc()->strict() && pc->sc()->isEvalContext()) + if (!pc->sc()->strict() && pc->sc()->isEvalContext()) { *redeclaredKind = isVarRedeclaredInEval(name, kind); + // We don't have position information at runtime. + *prevPos = DeclaredNameInfo::npos; + } return true; } @@ -1248,11 +1288,15 @@ Parser::tryDeclareVar(HandlePropertyName name, DeclarationKind kin template bool Parser::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, - bool* tryAnnexB) + uint32_t beginPos, bool* tryAnnexB) { Maybe redeclaredKind; - if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind)) + uint32_t unused; + if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos, + &redeclaredKind, &unused)) + { return false; + } if (!redeclaredKind && pc->isFunctionBox()) { ParseContext::Scope& funScope = pc->functionScope(); @@ -1324,11 +1368,12 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind case DeclarationKind::BodyLevelFunction: case DeclarationKind::ForOfVar: { Maybe redeclaredKind; - if (!tryDeclareVar(name, kind, &redeclaredKind)) + uint32_t prevPos; + if (!tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos)) return false; if (redeclaredKind) { - reportRedeclaration(name, *redeclaredKind, pos); + reportRedeclaration(name, *redeclaredKind, pos, prevPos); return false; } @@ -1345,7 +1390,7 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind return false; } - if (!pc->functionScope().addDeclaredName(pc, p, name, kind)) + if (!pc->functionScope().addDeclaredName(pc, p, name, kind, pos.begin)) return false; break; @@ -1368,7 +1413,7 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind (p->value()->kind() != DeclarationKind::LexicalFunction && p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction)) { - reportRedeclaration(name, p->value()->kind(), pos); + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } @@ -1376,7 +1421,7 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind // declaration that shadows the VarForAnnexBLexicalFunction. p->value()->alterKind(kind); } else { - if (!scope->addDeclaredName(pc, p, name, kind)) + if (!scope->addDeclaredName(pc, p, name, kind, pos.begin)) return false; } @@ -1416,7 +1461,7 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind if (pc->isFunctionExtraBodyVarScopeInnermost()) { DeclaredNamePtr p = pc->functionScope().lookupDeclaredName(name); if (p && DeclarationKindIsParameter(p->value()->kind())) { - reportRedeclaration(name, p->value()->kind(), pos); + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } } @@ -1433,12 +1478,12 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind p = scope->lookupDeclaredNameForAdd(name); MOZ_ASSERT(!p); } else { - reportRedeclaration(name, p->value()->kind(), pos); + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } } - if (!p && !scope->addDeclaredName(pc, p, name, kind)) + if (!p && !scope->addDeclaredName(pc, p, name, kind, pos.begin)) return false; break; @@ -2188,8 +2233,11 @@ Parser::declareFunctionThis() ParseContext::Scope& funScope = pc->functionScope(); AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis); MOZ_ASSERT(!p); - if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var)) + if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var, + DeclaredNameInfo::npos)) + { return false; + } funbox->setHasThisBinding(); } @@ -2231,8 +2279,11 @@ Parser::declareDotGeneratorName() ParseContext::Scope& funScope = pc->functionScope(); HandlePropertyName dotGenerator = context->names().dotGenerator; AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator); - if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var)) + if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var, + DeclaredNameInfo::npos)) + { return false; + } return true; } @@ -2481,8 +2532,11 @@ Parser::declareFunctionArgumentsObject() if (tryDeclareArguments) { AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName); if (!p) { - if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var)) + if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var, + DeclaredNameInfo::npos)) + { return false; + } funbox->declaredArguments = true; funbox->usesArguments = true; } else if (hasExtraBodyVarScope) { @@ -2964,8 +3018,8 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn if (!name) return false; - if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams, - &duplicatedParam)) + if (!notePositionalFormalParameter(funcpn, name, pos().begin, + disallowDuplicateParams, &duplicatedParam)) { return false; } @@ -3689,7 +3743,7 @@ Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHan // early error, do so. This 'var' binding would be assigned // the function object when its declaration is reached, not at // the start of the block. - if (!tryDeclareVarForAnnexBLexicalFunction(name, &tryAnnexB)) + if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB)) return null(); } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 824dddb58..6b5ade425 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -146,9 +146,9 @@ class ParseContext : public Nestable } MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name, - DeclarationKind kind) + DeclarationKind kind, uint32_t pos) { - return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind))); + return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos))); } // Remove all VarForAnnexBLexicalFunction declarations of a certain @@ -1437,15 +1437,17 @@ class Parser final : public ParserBase, private JS::AutoGCRooter bool hasValidSimpleStrictParameterNames(); - void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos); - bool notePositionalFormalParameter(Node fn, HandlePropertyName name, + void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos, + uint32_t prevPos); + bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos, bool disallowDuplicateParams, bool* duplicatedParam); bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct); mozilla::Maybe isVarRedeclaredInEval(HandlePropertyName name, DeclarationKind kind); - bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, - mozilla::Maybe* redeclaredKind); - bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, bool* tryAnnexB); + bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos, + mozilla::Maybe* redeclaredKind, uint32_t* prevPos); + bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, uint32_t beginPos, + bool* tryAnnexB); bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt, DeclarationKind kind, TokenPos pos); bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos); -- cgit v1.2.3 From d1c146adf23e317facd03cd5c097f12a69947392 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Mon, 10 Jun 2019 18:41:38 -0400 Subject: 1340148 - Disallow function redeclarations at module top level. --- js/src/frontend/NameAnalysisTypes.h | 2 ++ js/src/frontend/Parser.cpp | 28 +++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/NameAnalysisTypes.h b/js/src/frontend/NameAnalysisTypes.h index b94ca6cc0..2d327c827 100644 --- a/js/src/frontend/NameAnalysisTypes.h +++ b/js/src/frontend/NameAnalysisTypes.h @@ -78,6 +78,7 @@ enum class DeclarationKind : uint8_t Const, Import, BodyLevelFunction, + ModuleBodyLevelFunction, LexicalFunction, VarForAnnexBLexicalFunction, SimpleCatchParameter, @@ -95,6 +96,7 @@ DeclarationKindToBindingKind(DeclarationKind kind) case DeclarationKind::Var: case DeclarationKind::BodyLevelFunction: + case DeclarationKind::ModuleBodyLevelFunction: case DeclarationKind::VarForAnnexBLexicalFunction: case DeclarationKind::ForOfVar: return BindingKind::Var; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c96e8c065..d62a26f76 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -119,6 +119,7 @@ DeclarationKindString(DeclarationKind kind) case DeclarationKind::Import: return "import"; case DeclarationKind::BodyLevelFunction: + case DeclarationKind::ModuleBodyLevelFunction: case DeclarationKind::LexicalFunction: return "function"; case DeclarationKind::VarForAnnexBLexicalFunction: @@ -1380,6 +1381,24 @@ Parser::noteDeclaredName(HandlePropertyName name, DeclarationKind break; } + case DeclarationKind::ModuleBodyLevelFunction: { + MOZ_ASSERT(pc->atModuleLevel()); + + AddDeclaredNamePtr p = pc->varScope().lookupDeclaredNameForAdd(name); + if (p) { + reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); + return false; + } + + if (!pc->varScope().addDeclaredName(pc, p, name, kind, pos.begin)) + return false; + + // Body-level functions in modules are always closed over. + pc->varScope().lookupDeclaredName(name)->value()->setClosedOver(); + + break; + } + case DeclarationKind::FormalParameter: { // It is an early error if any non-positional formal parameter name // (e.g., destructuring formal parameter) is duplicated. @@ -3750,12 +3769,11 @@ Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHan if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos())) return null(); } else { - if (!noteDeclaredName(name, DeclarationKind::BodyLevelFunction, pos())) + DeclarationKind kind = pc->atModuleLevel() + ? DeclarationKind::ModuleBodyLevelFunction + : DeclarationKind::BodyLevelFunction; + if (!noteDeclaredName(name, kind, pos())) return null(); - - // Body-level functions in modules are always closed over. - if (pc->atModuleLevel()) - pc->varScope().lookupDeclaredName(name)->value()->setClosedOver(); } Node pn = handler.newFunctionStatement(); -- cgit v1.2.3 From 53e46b1e12ef01ccaabb3256738ea1eac74b7941 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 13 Jul 2019 21:33:52 -0400 Subject: 1216630 - Print class source when calling toString on the constructor. This is accomplished in the following ways. LazyScripts and JSScripts now have 4 offsets: - Source begin and end for the actual source. This is used for lazy parsing. - toString begin and end for toString. Some kinds of functions, like async, only have a different begin offset. Class constructors have different offsets for both begin and end. For syntactically present (i.e. non-default) constructors, the class source span is remembered directly on the LazyScript or JSScript. The toString implementation then splices out the substring directly. For default constructors, a new SRC_CLASS SrcNote type is added. It's binary and has as its arguments the begin and end offsets of the class expression or statement. MakeDefaultConstructor reads the note and overrides the cloned self-hosted function's source object. This is probably the least intrusive way to accomplish this. --- js/src/frontend/BytecodeCompiler.cpp | 16 ++++--- js/src/frontend/BytecodeEmitter.cpp | 10 ++++- js/src/frontend/FullParseHandler.h | 6 ++- js/src/frontend/ParseNode.h | 5 ++- js/src/frontend/Parser.cpp | 85 ++++++++++++++++++++++++++---------- js/src/frontend/Parser.h | 37 ++++++++++++++++ js/src/frontend/SharedContext.h | 11 +++++ js/src/frontend/SourceNotes.h | 3 +- js/src/frontend/SyntaxParseHandler.h | 2 +- 9 files changed, 140 insertions(+), 35 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index a4b7dba6b..e0bb64e05 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -77,7 +77,13 @@ class MOZ_STACK_CLASS BytecodeCompiler bool canLazilyParse(); bool createParser(); bool createSourceAndParser(Maybe parameterListEnd = Nothing()); - bool createScript(uint32_t preludeStart = 0); + + // If toString{Start,End} are not explicitly passed, assume the script's + // offsets in the source used to parse it are the same as what should be + // used to compute its Function.prototype.toString() value. + bool createScript(); + bool createScript(uint32_t toStringStart, uint32_t toStringEnd); + bool emplaceEmitter(Maybe& emitter, SharedContext* sharedContext); bool handleParseFailure(const Directives& newDirectives); bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment); @@ -242,11 +248,11 @@ BytecodeCompiler::createSourceAndParser(Maybe parameterListEnd /* = No } bool -BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */) +BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */, uint32_t postludeEnd /* = 0 */) { script = JSScript::Create(cx, options, sourceObject, /* sourceStart = */ 0, sourceBuffer.length(), - preludeStart); + preludeStart, postludeEnd); return script != nullptr; } @@ -458,7 +464,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun, if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); - if (!createScript(fn->pn_funbox->preludeStart)) + if (!createScript(fn->pn_funbox->preludeStart, fn->pn_funbox->postludeEnd)) return false; Maybe emitter; @@ -653,7 +659,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle lazy, const cha Rooted script(cx, JSScript::Create(cx, options, sourceObject, lazy->begin(), lazy->end(), - lazy->preludeStart())); + lazy->preludeStart(), lazy->postludeEnd())); if (!script) return false; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c15d67b7c..c788e0086 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7846,7 +7846,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) Rooted sourceObject(cx, script->sourceObject()); Rooted script(cx, JSScript::Create(cx, options, sourceObject, funbox->bufStart, funbox->bufEnd, - funbox->preludeStart)); + funbox->preludeStart, + funbox->postludeEnd)); if (!script) return false; @@ -10226,6 +10227,13 @@ BytecodeEmitter::emitClass(ParseNode* pn) return false; } } else { + // In the case of default class constructors, emit the start and end + // offsets in the source buffer as source notes so that when we + // actually make the constructor during execution, we can give it the + // correct toString output. + if (!newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(pn->pn_pos.begin), ptrdiff_t(pn->pn_pos.end))) + return false; + JSAtom *name = names ? names->innerBinding()->pn_atom : cx->names().empty; if (heritageExpression) { if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR)) diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index f0fc7700d..7ddd8d306 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -333,8 +333,10 @@ class FullParseHandler return literal; } - ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock) { - return new_(name, heritage, methodBlock); + ParseNode* newClass(ParseNode* name, ParseNode* heritage, ParseNode* methodBlock, + const TokenPos& pos) + { + return new_(name, heritage, methodBlock, pos); } ParseNode* newClassMethodList(uint32_t begin) { return new_(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1)); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index c0669d85e..1f20f3988 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -1309,8 +1309,9 @@ struct ClassNames : public BinaryNode { }; struct ClassNode : public TernaryNode { - ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock) - : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock) + ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock, + const TokenPos& pos) + : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock, pos) { MOZ_ASSERT_IF(names, names->is()); MOZ_ASSERT(methodsOrBlock->is() || diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index d62a26f76..299b8b93a 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -141,7 +141,8 @@ StatementKindIsBraced(StatementKind kind) kind == StatementKind::Switch || kind == StatementKind::Try || kind == StatementKind::Catch || - kind == StatementKind::Finally; + kind == StatementKind::Finally || + kind == StatementKind::Class; } void @@ -472,6 +473,7 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac startLine(1), startColumn(0), preludeStart(preludeStart), + postludeEnd(0), length(0), generatorKindBits_(GeneratorKindAsBits(generatorKind)), asyncKindBits_(AsyncKindAsBits(asyncKind)), @@ -542,10 +544,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt allowNewTarget_ = true; allowSuperProperty_ = fun->allowSuperProperty(); - if (kind == DerivedClassConstructor) { - setDerivedClassConstructor(); - allowSuperCall_ = true; - needsThisTDZChecks_ = true; + if (kind == ClassConstructor || kind == DerivedClassConstructor) { + auto stmt = enclosing->findInnermostStatement(); + MOZ_ASSERT(stmt); + stmt->setConstructorBox(this); + + if (kind == DerivedClassConstructor) { + setDerivedClassConstructor(); + allowSuperCall_ = true; + needsThisTDZChecks_ = true; + } } if (isGenexpLambda) @@ -565,6 +573,16 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt } } +void +FunctionBox::resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind) +{ + if (kind == ClassConstructor || kind == DerivedClassConstructor) { + auto stmt = enclosing->findInnermostStatement(); + MOZ_ASSERT(stmt); + stmt->clearConstructorBoxForAbortedSyntaxParse(this); + } +} + void FunctionBox::initWithEnclosingScope(Scope* enclosingScope) { @@ -3389,6 +3407,7 @@ Parser::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // correctness. parser->clearAbortedSyntaxParse(); usedNames.rewind(token); + funbox->resetForAbortedSyntaxParse(pc, kind); MOZ_ASSERT_IF(parser->context->isJSContext(), !parser->context->asJSContext()->isExceptionPending()); break; @@ -3670,14 +3689,14 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, error(JSMSG_CURLY_AFTER_BODY); return false; } - funbox->bufEnd = pos().end; + funbox->setEnd(pos().end); } else { #if !JS_HAS_EXPR_CLOSURES MOZ_ASSERT(kind == Arrow); #endif if (tokenStream.hadError()) return false; - funbox->bufEnd = pos().end; + funbox->setEnd(pos().end); if (kind == Statement && !matchOrInsertSemicolonAfterExpression()) return false; } @@ -6935,6 +6954,7 @@ Parser::classDefinition(YieldHandling yieldHandling, { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS)); + uint32_t classStartOffset = pos().begin; bool savedStrictness = setLocalStrictMode(true); TokenKind tt; @@ -6960,16 +6980,20 @@ Parser::classDefinition(YieldHandling yieldHandling, tokenStream.ungetToken(); } + // Push a ParseContext::ClassStatement to keep track of the constructor + // funbox. + ParseContext::ClassStatement classStmt(pc); + RootedAtom propAtom(context); // A named class creates a new lexical scope with a const binding of the - // class name. - Maybe classStmt; - Maybe classScope; + // class name for the "inner name". + Maybe innerScopeStmt; + Maybe innerScope; if (name) { - classStmt.emplace(pc, StatementKind::Block); - classScope.emplace(this); - if (!classScope->init(pc)) + innerScopeStmt.emplace(pc, StatementKind::Block); + innerScope.emplace(this); + if (!innerScope->init(pc)) return null(); } @@ -6996,7 +7020,6 @@ Parser::classDefinition(YieldHandling yieldHandling, if (!classMethods) return null(); - bool seenConstructor = false; for (;;) { TokenKind tt; if (!tokenStream.getToken(&tt)) @@ -7048,16 +7071,17 @@ Parser::classDefinition(YieldHandling yieldHandling, propType = PropertyType::GetterNoExpressionClosure; if (propType == PropertyType::Setter) propType = PropertyType::SetterNoExpressionClosure; - if (!isStatic && propAtom == context->names().constructor) { + + bool isConstructor = !isStatic && propAtom == context->names().constructor; + if (isConstructor) { if (propType != PropertyType::Method) { errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } - if (seenConstructor) { + if (classStmt.constructorBox()) { errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor"); return null(); } - seenConstructor = true; propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor; } else if (isStatic && propAtom == context->names().prototype) { errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); @@ -7082,7 +7106,12 @@ Parser::classDefinition(YieldHandling yieldHandling, if (!tokenStream.isCurrentTokenType(TOK_RB)) funName = propAtom; } - Node fn = methodDefinition(nameOffset, propType, funName); + + // Calling toString on constructors need to return the source text for + // the entire class. The end offset is unknown at this point in + // parsing and will be amended when class parsing finishes below. + Node fn = methodDefinition(isConstructor ? classStartOffset : nameOffset, + propType, funName); if (!fn) return null(); @@ -7093,6 +7122,15 @@ Parser::classDefinition(YieldHandling yieldHandling, return null(); } + // Amend the postlude offset for the constructor now that we've finished + // parsing the class. + uint32_t classEndOffset = pos().end; + if (FunctionBox* ctorbox = classStmt.constructorBox()) { + if (ctorbox->function()->isInterpretedLazy()) + ctorbox->function()->lazyScript()->setPostludeEnd(classEndOffset); + ctorbox->postludeEnd = classEndOffset; + } + Node nameNode = null(); Node methodsOrBlock = classMethods; if (name) { @@ -7104,15 +7142,15 @@ Parser::classDefinition(YieldHandling yieldHandling, if (!innerName) return null(); - Node classBlock = finishLexicalScope(*classScope, classMethods); + Node classBlock = finishLexicalScope(*innerScope, classMethods); if (!classBlock) return null(); methodsOrBlock = classBlock; // Pop the inner scope. - classScope.reset(); - classStmt.reset(); + innerScope.reset(); + innerScopeStmt.reset(); Node outerName = null(); if (classContext == ClassStatement) { @@ -7132,7 +7170,8 @@ Parser::classDefinition(YieldHandling yieldHandling, MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness)); - return handler.newClass(nameNode, classHeritage, methodsOrBlock); + return handler.newClass(nameNode, classHeritage, methodsOrBlock, + TokenPos(classStartOffset, classEndOffset)); } template @@ -8388,7 +8427,7 @@ Parser::generatorComprehensionLambda(unsigned begin) uint32_t end = pos().end; handler.setBeginPosition(comp, begin); handler.setEndPosition(comp, end); - genFunbox->bufEnd = end; + genFunbox->setEnd(end); handler.addStatementToList(body, comp); handler.setEndPosition(body, end); handler.setBeginPosition(genfn, begin); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 6b5ade425..efb543efa 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -85,6 +85,31 @@ class ParseContext : public Nestable } }; + class ClassStatement : public Statement + { + FunctionBox* constructorBox_; + + public: + explicit ClassStatement(ParseContext* pc) + : Statement(pc, StatementKind::Class), + constructorBox_(nullptr) + { } + + void clearConstructorBoxForAbortedSyntaxParse(FunctionBox* funbox) { + MOZ_ASSERT(constructorBox_ == funbox); + constructorBox_ = nullptr; + } + + void setConstructorBox(FunctionBox* funbox) { + MOZ_ASSERT(!constructorBox_); + constructorBox_ = funbox; + } + + FunctionBox* constructorBox() const { + return constructorBox_; + } + }; + // The intra-function scope stack. // // Tracks declared and used names within a scope. @@ -432,6 +457,11 @@ class ParseContext : public Nestable return Statement::findNearest(innermostStatement_, predicate); } + template + T* findInnermostStatement() { + return Statement::findNearest(innermostStatement_); + } + AtomVector& positionalFormalParameterNames() { return *positionalFormalParameterNames_; } @@ -532,6 +562,13 @@ ParseContext::Statement::is() const return kind_ == StatementKind::Label; } +template <> +inline bool +ParseContext::Statement::is() const +{ + return kind_ == StatementKind::Class; +} + template inline T& ParseContext::Statement::as() diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index f5a74e18c..213a0a461 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -38,6 +38,7 @@ enum class StatementKind : uint8_t ForOfLoop, DoLoop, WhileLoop, + Class, // Used only by BytecodeEmitter. Spread @@ -451,6 +452,7 @@ class FunctionBox : public ObjectBox, public SharedContext uint32_t startLine; uint32_t startColumn; uint32_t preludeStart; + uint32_t postludeEnd; uint16_t length; uint8_t generatorKindBits_; /* The GeneratorKind of this function. */ @@ -501,6 +503,7 @@ class FunctionBox : public ObjectBox, public SharedContext void initFromLazyFunction(); void initStandaloneFunction(Scope* enclosingScope); void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind); + void resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind); ObjectBox* toObjectBox() override { return this; } JSFunction* function() const { return &object->as(); } @@ -603,6 +606,14 @@ class FunctionBox : public ObjectBox, public SharedContext tokenStream.srcCoords.lineNumAndColumnIndex(bufStart, &startLine, &startColumn); } + void setEnd(uint32_t end) { + // For all functions except class constructors, the buffer and + // postlude ending positions are the same. Class constructors override + // the postlude ending position with the end of the class definition. + bufEnd = end; + postludeEnd = end; + } + void trace(JSTracer* trc) override; }; diff --git a/js/src/frontend/SourceNotes.h b/js/src/frontend/SourceNotes.h index dd2a95ad1..6ae184ae4 100644 --- a/js/src/frontend/SourceNotes.h +++ b/js/src/frontend/SourceNotes.h @@ -56,13 +56,14 @@ namespace js { M(SRC_NEXTCASE, "nextcase", 1) /* Distance forward from one CASE in a CONDSWITCH to \ the next. */ \ M(SRC_ASSIGNOP, "assignop", 0) /* += or another assign-op follows. */ \ + M(SRC_CLASS_SPAN, "class", 2) /* The starting and ending offsets for the class, used \ + for toString correctness for default ctors. */ \ M(SRC_TRY, "try", 1) /* JSOP_TRY, offset points to goto at the end of the \ try block. */ \ /* All notes above here are "gettable". See SN_IS_GETTABLE below. */ \ M(SRC_COLSPAN, "colspan", 1) /* Number of columns this opcode spans. */ \ M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \ M(SRC_SETLINE, "setline", 1) /* A file-absolute source line number note. */ \ - M(SRC_UNUSED20, "unused20", 0) /* Unused. */ \ M(SRC_UNUSED21, "unused21", 0) /* Unused. */ \ M(SRC_UNUSED22, "unused22", 0) /* Unused. */ \ M(SRC_UNUSED23, "unused23", 0) /* Unused. */ \ diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index f492bab66..895ba6416 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -289,7 +289,7 @@ class SyntaxParseHandler Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; } Node newClassMethodList(uint32_t begin) { return NodeGeneric; } Node newClassNames(Node outer, Node inner, const TokenPos& pos) { return NodeGeneric; } - Node newClass(Node name, Node heritage, Node methodBlock) { return NodeGeneric; } + Node newClass(Node name, Node heritage, Node methodBlock, const TokenPos& pos) { return NodeGeneric; } Node newNewTarget(Node newHolder, Node targetHolder) { return NodeGeneric; } Node newPosHolder(const TokenPos& pos) { return NodeGeneric; } -- cgit v1.2.3 From 22dba02b7bcab4d6dfe6b326ecc0a746e5a87191 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 13 Jul 2019 23:02:36 -0400 Subject: 1216630 - Rename preludeStart and postludeEnd to toStringStart and toStringEnd and misc fixes. --- js/src/frontend/BytecodeCompiler.cpp | 14 ++++-- js/src/frontend/BytecodeEmitter.cpp | 4 +- js/src/frontend/Parser.cpp | 82 ++++++++++++++++++------------------ js/src/frontend/Parser.h | 18 ++++---- js/src/frontend/SharedContext.h | 12 +++--- 5 files changed, 68 insertions(+), 62 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index e0bb64e05..a1abbfeda 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -248,11 +248,17 @@ BytecodeCompiler::createSourceAndParser(Maybe parameterListEnd /* = No } bool -BytecodeCompiler::createScript(uint32_t preludeStart /* = 0 */, uint32_t postludeEnd /* = 0 */) +BytecodeCompiler::createScript() +{ + return createScript(0, sourceBuffer.length()); +} + +bool +BytecodeCompiler::createScript(uint32_t toStringStart, uint32_t toStringEnd) { script = JSScript::Create(cx, options, sourceObject, /* sourceStart = */ 0, sourceBuffer.length(), - preludeStart, postludeEnd); + toStringStart, toStringEnd); return script != nullptr; } @@ -464,7 +470,7 @@ BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun, if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); - if (!createScript(fn->pn_funbox->preludeStart, fn->pn_funbox->postludeEnd)) + if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd)) return false; Maybe emitter; @@ -659,7 +665,7 @@ frontend::CompileLazyFunction(JSContext* cx, Handle lazy, const cha Rooted script(cx, JSScript::Create(cx, options, sourceObject, lazy->begin(), lazy->end(), - lazy->preludeStart(), lazy->postludeEnd())); + lazy->toStringStart(), lazy->toStringEnd())); if (!script) return false; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c788e0086..7582e8df1 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7846,8 +7846,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) Rooted sourceObject(cx, script->sourceObject()); Rooted script(cx, JSScript::Create(cx, options, sourceObject, funbox->bufStart, funbox->bufEnd, - funbox->preludeStart, - funbox->postludeEnd)); + funbox->toStringStart, + funbox->toStringEnd)); if (!script) return false; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 299b8b93a..1be57f8f5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -458,7 +458,7 @@ UsedNameTracker::rewind(RewindToken token) } FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, - JSFunction* fun, uint32_t preludeStart, + JSFunction* fun, uint32_t toStringStart, Directives directives, bool extraWarnings, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) : ObjectBox(fun, traceListHead), @@ -472,8 +472,8 @@ FunctionBox::FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* trac bufEnd(0), startLine(1), startColumn(0), - preludeStart(preludeStart), - postludeEnd(0), + toStringStart(toStringStart), + toStringEnd(0), length(0), generatorKindBits_(GeneratorKindAsBits(generatorKind)), asyncKindBits_(AsyncKindAsBits(asyncKind)), @@ -888,7 +888,7 @@ Parser::newObjectBox(JSObject* obj) template FunctionBox* -Parser::newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart, +Parser::newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, Directives inheritedDirectives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB) @@ -904,7 +904,7 @@ Parser::newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeS * function. */ FunctionBox* funbox = - alloc.new_(context, alloc, traceListHead, fun, preludeStart, + alloc.new_(context, alloc, traceListHead, fun, toStringStart, inheritedDirectives, options().extraWarningsOption, generatorKind, asyncKind); if (!funbox) { @@ -2404,7 +2404,7 @@ Parser::finishFunction(bool isStandaloneFunction /* = false LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(), pc->innerFunctionsForLazy, versionNumber(), funbox->bufStart, funbox->bufEnd, - funbox->preludeStart, + funbox->toStringStart, funbox->startLine, funbox->startColumn); if (!lazy) return false; @@ -2495,7 +2495,7 @@ Parser::standaloneFunction(HandleFunction fun, return null(); fn->pn_body = argsbody; - FunctionBox* funbox = newFunctionBox(fn, fun, /* preludeStart = */ 0, inheritedDirectives, + FunctionBox* funbox = newFunctionBox(fn, fun, /* toStringStart = */ 0, inheritedDirectives, generatorKind, asyncKind, /* tryAnnexB = */ false); if (!funbox) return null(); @@ -3158,7 +3158,7 @@ Parser::functionArguments(YieldHandling yieldHandling, FunctionSyn template <> bool -Parser::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeStart, +Parser::skipLazyInnerFunction(ParseNode* pn, uint32_t toStringStart, FunctionSyntaxKind kind, bool tryAnnexB) { // When a lazily-parsed function is called, we only fully parse (and emit) @@ -3168,7 +3168,7 @@ Parser::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS RootedFunction fun(context, handler.nextLazyInnerFunction()); MOZ_ASSERT(!fun->isLegacyGenerator()); - FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, Directives(/* strict = */ false), + FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false), fun->generatorKind(), fun->asyncKind(), tryAnnexB); if (!funbox) return false; @@ -3205,7 +3205,7 @@ Parser::skipLazyInnerFunction(ParseNode* pn, uint32_t preludeS template <> bool -Parser::skipLazyInnerFunction(Node pn, uint32_t preludeStart, +Parser::skipLazyInnerFunction(Node pn, uint32_t toStringStart, FunctionSyntaxKind kind, bool tryAnnexB) { MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing"); @@ -3282,7 +3282,7 @@ Parser::templateLiteral(YieldHandling yieldHandling) template typename ParseHandler::Node -Parser::functionDefinition(uint32_t preludeStart, Node pn, InHandling inHandling, +Parser::functionDefinition(uint32_t toStringStart, Node pn, InHandling inHandling, YieldHandling yieldHandling, HandleAtom funName, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, @@ -3295,7 +3295,7 @@ Parser::functionDefinition(uint32_t preludeStart, Node pn, InHandl // functions, which are also lazy. Instead, their free variables and // source extents are recorded and may be skipped. if (handler.canSkipLazyInnerFunctions()) { - if (!skipLazyInnerFunction(pn, preludeStart, kind, tryAnnexB)) + if (!skipLazyInnerFunction(pn, toStringStart, kind, tryAnnexB)) return null(); return pn; } @@ -3328,7 +3328,7 @@ Parser::functionDefinition(uint32_t preludeStart, Node pn, InHandl // reparse a function due to failed syntax parsing and encountering new // "use foo" directives. while (true) { - if (trySyntaxParseInnerFunction(pn, fun, preludeStart, inHandling, yieldHandling, kind, + if (trySyntaxParseInnerFunction(pn, fun, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, directives, &newDirectives)) { @@ -3357,7 +3357,7 @@ Parser::functionDefinition(uint32_t preludeStart, Node pn, InHandl template <> bool Parser::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunction fun, - uint32_t preludeStart, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, @@ -3391,13 +3391,13 @@ Parser::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // Make a FunctionBox before we enter the syntax parser, because |pn| // still expects a FunctionBox to be attached to it during BCE, and // the syntax parser cannot attach one to it. - FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, inheritedDirectives, + FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives, generatorKind, asyncKind, tryAnnexB); if (!funbox) return false; funbox->initWithEnclosingParseContext(pc, kind); - if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, preludeStart, + if (!parser->innerFunction(SyntaxParseHandler::NodeGeneric, pc, funbox, toStringStart, inHandling, yieldHandling, kind, inheritedDirectives, newDirectives)) { @@ -3426,14 +3426,14 @@ Parser::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct } while (false); // We failed to do a syntax parse above, so do the full parse. - return innerFunction(pn, pc, fun, preludeStart, inHandling, yieldHandling, kind, + return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template <> bool Parser::trySyntaxParseInnerFunction(Node pn, HandleFunction fun, - uint32_t preludeStart, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, @@ -3444,14 +3444,14 @@ Parser::trySyntaxParseInnerFunction(Node pn, HandleFunction Directives* newDirectives) { // This is already a syntax parser, so just parse the inner function. - return innerFunction(pn, pc, fun, preludeStart, inHandling, yieldHandling, kind, + return innerFunction(pn, pc, fun, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template bool Parser::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, - uint32_t preludeStart, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, Directives inheritedDirectives, Directives* newDirectives) @@ -3475,7 +3475,7 @@ Parser::innerFunction(Node pn, ParseContext* outerpc, FunctionBox* template bool Parser::innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, - uint32_t preludeStart, + uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, @@ -3487,13 +3487,13 @@ Parser::innerFunction(Node pn, ParseContext* outerpc, HandleFuncti // parser. In that case, outerpc is a ParseContext from the full parser // instead of the current top of the stack of the syntax parser. - FunctionBox* funbox = newFunctionBox(pn, fun, preludeStart, inheritedDirectives, + FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, inheritedDirectives, generatorKind, asyncKind, tryAnnexB); if (!funbox) return false; funbox->initWithEnclosingParseContext(outerpc, kind); - return innerFunction(pn, outerpc, funbox, preludeStart, inHandling, yieldHandling, kind, + return innerFunction(pn, outerpc, funbox, toStringStart, inHandling, yieldHandling, kind, inheritedDirectives, newDirectives); } @@ -3529,7 +3529,7 @@ Parser::standaloneLazyFunction(HandleFunction fun, bool strict return null(); Directives directives(strict); - FunctionBox* funbox = newFunctionBox(pn, fun, /* preludeStart = */ 0, directives, + FunctionBox* funbox = newFunctionBox(pn, fun, /* toStringStart = */ 0, directives, generatorKind, asyncKind, /* tryAnnexB = */ false); if (!funbox) return null(); @@ -3716,7 +3716,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, template typename ParseHandler::Node -Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHandling, +Parser::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling, DefaultHandling defaultHandling, FunctionAsyncKind asyncKind) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); @@ -3800,13 +3800,13 @@ Parser::functionStmt(uint32_t preludeStart, YieldHandling yieldHan return null(); YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); - return functionDefinition(preludeStart, pn, InAllowed, newYieldHandling, + return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling, name, Statement, generatorKind, asyncKind, tryAnnexB); } template typename ParseHandler::Node -Parser::functionExpr(uint32_t preludeStart, InvokedPrediction invoked, +Parser::functionExpr(uint32_t toStringStart, InvokedPrediction invoked, FunctionAsyncKind asyncKind) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); @@ -3845,7 +3845,7 @@ Parser::functionExpr(uint32_t preludeStart, InvokedPrediction invo if (invoked) pn = handler.setLikelyIIFE(pn); - return functionDefinition(preludeStart, pn, InAllowed, yieldHandling, name, Expression, + return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, name, Expression, generatorKind, asyncKind); } @@ -7122,13 +7122,13 @@ Parser::classDefinition(YieldHandling yieldHandling, return null(); } - // Amend the postlude offset for the constructor now that we've finished - // parsing the class. + // Amend the toStringEnd offset for the constructor now that we've + // finished parsing the class. uint32_t classEndOffset = pos().end; if (FunctionBox* ctorbox = classStmt.constructorBox()) { if (ctorbox->function()->isInterpretedLazy()) - ctorbox->function()->lazyScript()->setPostludeEnd(classEndOffset); - ctorbox->postludeEnd = classEndOffset; + ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset); + ctorbox->toStringEnd = classEndOffset; } Node nameNode = null(); @@ -7507,9 +7507,9 @@ Parser::statementListItem(YieldHandling yieldHandling, if (!tokenStream.peekTokenSameLine(&nextSameLine)) return null(); if (nextSameLine == TOK_FUNCTION) { - uint32_t preludeStart = pos().begin; + uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction); + return functionStmt(toStringStart, yieldHandling, NameRequired, AsyncFunction); } } @@ -8053,7 +8053,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.getToken(&next, TokenStream::Operand)) return null(); - uint32_t preludeStart = pos().begin; + uint32_t toStringStart = pos().begin; tokenStream.ungetToken(); GeneratorKind generatorKind = NotGenerator; @@ -8078,7 +8078,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!pn) return null(); - Node arrowFunc = functionDefinition(preludeStart, pn, inHandling, yieldHandling, nullptr, + Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr, Arrow, generatorKind, asyncKind); if (!arrowFunc) return null(); @@ -8391,7 +8391,7 @@ Parser::generatorComprehensionLambda(unsigned begin) // Create box for fun->object early to root it. Directives directives(/* strict = */ outerpc->sc()->strict()); - FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* preludeStart = */ 0, directives, + FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* toStringStart = */ 0, directives, StarGenerator, SyncFunction, /* tryAnnexB = */ false); if (!genFunbox) return null(); @@ -9662,7 +9662,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* template typename ParseHandler::Node -Parser::methodDefinition(uint32_t preludeStart, PropertyType propType, +Parser::methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName) { FunctionSyntaxKind kind; @@ -9716,7 +9716,7 @@ Parser::methodDefinition(uint32_t preludeStart, PropertyType propT if (!pn) return null(); - return functionDefinition(preludeStart, pn, InAllowed, yieldHandling, funName, + return functionDefinition(toStringStart, pn, InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind); } @@ -9845,9 +9845,9 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling return null(); if (nextSameLine == TOK_FUNCTION) { - uint32_t preludeStart = pos().begin; + uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TOK_FUNCTION); - return functionExpr(preludeStart, PredictUninvoked, AsyncFunction); + return functionExpr(toStringStart, PredictUninvoked, AsyncFunction); } } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index efb543efa..65e46a65e 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1081,7 +1081,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter * cx->tempLifoAlloc. */ ObjectBox* newObjectBox(JSObject* obj); - FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t preludeStart, + FunctionBox* newFunctionBox(Node fn, JSFunction* fun, uint32_t toStringStart, Directives directives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB); @@ -1152,7 +1152,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter // Parse an inner function given an enclosing ParseContext and a // FunctionBox for the inner function. - bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t preludeStart, + bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, Directives inheritedDirectives, Directives* newDirectives); @@ -1186,10 +1186,10 @@ class Parser final : public ParserBase, private JS::AutoGCRooter * Some parsers have two versions: an always-inlined version (with an 'i' * suffix) and a never-inlined version (with an 'n' suffix). */ - Node functionStmt(uint32_t preludeStart, + Node functionStmt(uint32_t toStringStart, YieldHandling yieldHandling, DefaultHandling defaultHandling, FunctionAsyncKind asyncKind = SyncFunction); - Node functionExpr(uint32_t preludeStart, InvokedPrediction invoked = PredictUninvoked, + Node functionExpr(uint32_t toStringStart, InvokedPrediction invoked = PredictUninvoked, FunctionAsyncKind asyncKind = SyncFunction); Node statementList(YieldHandling yieldHandling); @@ -1338,7 +1338,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter bool tryNewTarget(Node& newTarget); bool checkAndMarkSuperScope(); - Node methodDefinition(uint32_t preludeStart, PropertyType propType, HandleAtom funName); + Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName); /* * Additional JS parsers. @@ -1346,7 +1346,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind, Node funcpn); - Node functionDefinition(uint32_t preludeStart, Node pn, + Node functionDefinition(uint32_t toStringStart, Node pn, InHandling inHandling, YieldHandling yieldHandling, HandleAtom name, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, @@ -1438,14 +1438,14 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node newDotGeneratorName(); bool declareDotGeneratorName(); - bool skipLazyInnerFunction(Node pn, uint32_t preludeStart, FunctionSyntaxKind kind, + bool skipLazyInnerFunction(Node pn, uint32_t toStringStart, FunctionSyntaxKind kind, bool tryAnnexB); - bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t preludeStart, + bool innerFunction(Node pn, ParseContext* outerpc, HandleFunction fun, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives); - bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t preludeStart, + bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 213a0a461..3499a53fb 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -451,8 +451,8 @@ class FunctionBox : public ObjectBox, public SharedContext uint32_t bufEnd; uint32_t startLine; uint32_t startColumn; - uint32_t preludeStart; - uint32_t postludeEnd; + uint32_t toStringStart; + uint32_t toStringEnd; uint16_t length; uint8_t generatorKindBits_; /* The GeneratorKind of this function. */ @@ -482,7 +482,7 @@ class FunctionBox : public ObjectBox, public SharedContext FunctionContextFlags funCxFlags; FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun, - uint32_t preludeStart, Directives directives, bool extraWarnings, + uint32_t toStringStart, Directives directives, bool extraWarnings, GeneratorKind generatorKind, FunctionAsyncKind asyncKind); MutableHandle namedLambdaBindings() { @@ -608,10 +608,10 @@ class FunctionBox : public ObjectBox, public SharedContext void setEnd(uint32_t end) { // For all functions except class constructors, the buffer and - // postlude ending positions are the same. Class constructors override - // the postlude ending position with the end of the class definition. + // toString ending positions are the same. Class constructors override + // the toString ending position with the end of the class definition. bufEnd = end; - postludeEnd = end; + toStringEnd = end; } void trace(JSTracer* trc) override; -- cgit v1.2.3 From 92ca89f5f791656b593596e0ce992cf1e1e42b41 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 13 Jul 2019 23:06:35 -0400 Subject: 1357483 - Stop changing the end position of parenthesized expression ParseNodes. --- js/src/frontend/Parser.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 1be57f8f5..ec4a975e6 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -9820,7 +9820,6 @@ Parser::primaryExpr(YieldHandling yieldHandling, TripledotHandling if (!expr) return null(); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); - handler.setEndPosition(expr, pos().end); return handler.parenthesize(expr); } -- cgit v1.2.3 From fe80b3d80d20241ff03338de4351d580796fd2c7 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 13 Jul 2019 23:20:19 -0400 Subject: 1357506 - Remove assert that constructorBox can only be set once when parsing classes. Both asm.js and syntax parsing can abort and rewind parsing of an inner function. The bookkeeping to make sure that a class's constructor FunctionBox is only set once is not worth it -- duplicate constructor definitions already throw an early error. --- js/src/frontend/Parser.cpp | 17 +++-------------- js/src/frontend/Parser.h | 21 +++------------------ js/src/frontend/SharedContext.h | 1 - 3 files changed, 6 insertions(+), 33 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index ec4a975e6..01ab3f64c 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -547,7 +547,7 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt if (kind == ClassConstructor || kind == DerivedClassConstructor) { auto stmt = enclosing->findInnermostStatement(); MOZ_ASSERT(stmt); - stmt->setConstructorBox(this); + stmt->constructorBox = this; if (kind == DerivedClassConstructor) { setDerivedClassConstructor(); @@ -573,16 +573,6 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt } } -void -FunctionBox::resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind) -{ - if (kind == ClassConstructor || kind == DerivedClassConstructor) { - auto stmt = enclosing->findInnermostStatement(); - MOZ_ASSERT(stmt); - stmt->clearConstructorBoxForAbortedSyntaxParse(this); - } -} - void FunctionBox::initWithEnclosingScope(Scope* enclosingScope) { @@ -3407,7 +3397,6 @@ Parser::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // correctness. parser->clearAbortedSyntaxParse(); usedNames.rewind(token); - funbox->resetForAbortedSyntaxParse(pc, kind); MOZ_ASSERT_IF(parser->context->isJSContext(), !parser->context->asJSContext()->isExceptionPending()); break; @@ -7078,7 +7067,7 @@ Parser::classDefinition(YieldHandling yieldHandling, errorAt(nameOffset, JSMSG_BAD_METHOD_DEF); return null(); } - if (classStmt.constructorBox()) { + if (classStmt.constructorBox) { errorAt(nameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor"); return null(); } @@ -7125,7 +7114,7 @@ Parser::classDefinition(YieldHandling yieldHandling, // Amend the toStringEnd offset for the constructor now that we've // finished parsing the class. uint32_t classEndOffset = pos().end; - if (FunctionBox* ctorbox = classStmt.constructorBox()) { + if (FunctionBox* ctorbox = classStmt.constructorBox) { if (ctorbox->function()->isInterpretedLazy()) ctorbox->function()->lazyScript()->setToStringEnd(classEndOffset); ctorbox->toStringEnd = classEndOffset; diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 65e46a65e..7866bc4fd 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -85,29 +85,14 @@ class ParseContext : public Nestable } }; - class ClassStatement : public Statement + struct ClassStatement : public Statement { - FunctionBox* constructorBox_; + FunctionBox* constructorBox; - public: explicit ClassStatement(ParseContext* pc) : Statement(pc, StatementKind::Class), - constructorBox_(nullptr) + constructorBox(nullptr) { } - - void clearConstructorBoxForAbortedSyntaxParse(FunctionBox* funbox) { - MOZ_ASSERT(constructorBox_ == funbox); - constructorBox_ = nullptr; - } - - void setConstructorBox(FunctionBox* funbox) { - MOZ_ASSERT(!constructorBox_); - constructorBox_ = funbox; - } - - FunctionBox* constructorBox() const { - return constructorBox_; - } }; // The intra-function scope stack. diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 3499a53fb..013444690 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -503,7 +503,6 @@ class FunctionBox : public ObjectBox, public SharedContext void initFromLazyFunction(); void initStandaloneFunction(Scope* enclosingScope); void initWithEnclosingParseContext(ParseContext* enclosing, FunctionSyntaxKind kind); - void resetForAbortedSyntaxParse(ParseContext* enclosing, FunctionSyntaxKind kind); ObjectBox* toObjectBox() override { return this; } JSFunction* function() const { return &object->as(); } -- cgit v1.2.3 From ccb322db6d1f14c3013bacb01dcb064a3f7fa28f Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 10:15:10 -0400 Subject: 420857 - Part 1: Report the position of opening brace for missing brace error in function body. --- js/src/frontend/Parser.cpp | 44 ++++++++++++++++++++++++++++++++++++++------ js/src/frontend/Parser.h | 2 ++ 2 files changed, 40 insertions(+), 6 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 01ab3f64c..9aed88ce9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -1012,6 +1012,35 @@ Parser::hasValidSimpleStrictParameterNames() return true; } +template +void +Parser::reportMissingClosing(unsigned errorNumber, unsigned noteNumber, + uint32_t openedPos) +{ + auto notes = MakeUnique(); + if (!notes) + return; + + uint32_t line, column; + tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column); + + const size_t MaxWidth = sizeof("4294967295"); + char columnNumber[MaxWidth]; + SprintfLiteral(columnNumber, "%" PRIu32, column); + char lineNumber[MaxWidth]; + SprintfLiteral(lineNumber, "%" PRIu32, line); + + if (!notes->addNoteASCII(pc->sc()->context, + getFilename(), line, column, + GetErrorMessage, nullptr, + noteNumber, lineNumber, columnNumber)) + { + return; + } + + errorWithNotes(Move(notes), errorNumber); +} + template void Parser::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, @@ -1039,11 +1068,11 @@ Parser::reportRedeclaration(HandlePropertyName name, DeclarationKi char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); - if (!notes->addNoteLatin1(pc->sc()->context, - getFilename(), line, column, - GetErrorMessage, nullptr, - JSMSG_REDECLARED_PREV, - lineNumber, columnNumber)) + if (!notes->addNoteASCII(pc->sc()->context, + getFilename(), line, column, + GetErrorMessage, nullptr, + JSMSG_REDECLARED_PREV, + lineNumber, columnNumber)) { return; } @@ -3613,6 +3642,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::Operand)) return false; + uint32_t openedPos = 0; if (tt != TOK_LC) { if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method || kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure || @@ -3634,6 +3664,8 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, tokenStream.ungetToken(); bodyType = ExpressionBody; funbox->setIsExprBody(); + } else { + openedPos = pos().begin; } // Arrow function parameters inherit yieldHandling from the enclosing @@ -3675,7 +3707,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand)) return false; if (!matched) { - error(JSMSG_CURLY_AFTER_BODY); + reportMissingClosing(JSMSG_CURLY_AFTER_BODY, JSMSG_CURLY_OPENED, openedPos); return false; } funbox->setEnd(pos().end); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 7866bc4fd..b1d3bdee0 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1459,6 +1459,8 @@ class Parser final : public ParserBase, private JS::AutoGCRooter bool hasValidSimpleStrictParameterNames(); + void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos); + void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos, uint32_t prevPos); bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos, -- cgit v1.2.3 From 3e1a8a74f2fc69f9df6f18ab19f5095722da7a60 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 10:22:32 -0400 Subject: 420857 - Part 2: Report the position of opening brace for missing brace error in block. --- js/src/frontend/Parser.cpp | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 9aed88ce9..ef352ec35 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -66,29 +66,32 @@ using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr; // Read a token. Report an error and return null() if that token doesn't match // to the condition. Do not use MUST_MATCH_TOKEN_INTERNAL directly. -#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorNumber) \ +#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport) \ JS_BEGIN_MACRO \ TokenKind token; \ if (!tokenStream.getToken(&token, modifier)) \ return null(); \ if (!(cond)) { \ - error(errorNumber); \ + errorReport; \ return null(); \ } \ JS_END_MACRO #define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \ - MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorNumber) + MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber)) #define MUST_MATCH_TOKEN(tt, errorNumber) \ MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber) #define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \ - MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, errorNumber) + MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber)) #define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \ MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber) +#define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \ + MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport) + template static inline void PropagateTransitiveParseFlags(const T* inner, U* outer) @@ -3703,13 +3706,9 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, } if (bodyType == StatementListBody) { - bool matched; - if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand)) - return false; - if (!matched) { - reportMissingClosing(JSMSG_CURLY_AFTER_BODY, JSMSG_CURLY_OPENED, openedPos); - return false; - } + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_BODY, + JSMSG_CURLY_OPENED, openedPos)); funbox->setEnd(pos().end); } else { #if !JS_HAS_EXPR_CLOSURES @@ -4481,6 +4480,7 @@ typename ParseHandler::Node Parser::blockStatement(YieldHandling yieldHandling, unsigned errorNumber) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); + uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc, StatementKind::Block); ParseContext::Scope scope(this); @@ -4491,7 +4491,9 @@ Parser::blockStatement(YieldHandling yieldHandling, unsigned error if (!list) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, + openedPos)); return finishLexicalScope(scope, list); } @@ -6743,6 +6745,8 @@ Parser::tryStatement(YieldHandling yieldHandling) { MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); + uint32_t openedPos = pos().begin; + ParseContext::Statement stmt(pc, StatementKind::Try); ParseContext::Scope scope(this); if (!scope.init(pc)) @@ -6756,7 +6760,9 @@ Parser::tryStatement(YieldHandling yieldHandling) if (!innerBlock) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_TRY, + JSMSG_CURLY_OPENED, openedPos)); } bool hasUnconditionalCatch = false; @@ -6872,6 +6878,8 @@ Parser::tryStatement(YieldHandling yieldHandling) if (tt == TOK_FINALLY) { MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); + uint32_t openedPos = pos().begin; + ParseContext::Statement stmt(pc, StatementKind::Finally); ParseContext::Scope scope(this); if (!scope.init(pc)) @@ -6885,7 +6893,9 @@ Parser::tryStatement(YieldHandling yieldHandling) if (!finallyBlock) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY, + JSMSG_CURLY_OPENED, openedPos)); } else { tokenStream.ungetToken(); } @@ -6902,6 +6912,8 @@ typename ParseHandler::Node Parser::catchBlockStatement(YieldHandling yieldHandling, ParseContext::Scope& catchParamScope) { + uint32_t openedPos = pos().begin; + ParseContext::Statement stmt(pc, StatementKind::Block); // ES 13.15.7 CatchClauseEvaluation @@ -6921,7 +6933,9 @@ Parser::catchBlockStatement(YieldHandling yieldHandling, if (!list) return null(); - MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand, + reportMissingClosing(JSMSG_CURLY_AFTER_CATCH, + JSMSG_CURLY_OPENED, openedPos)); // The catch parameter names are not bound in the body scope, so remove // them before generating bindings. -- cgit v1.2.3 From 10227d77bfccda845a63e20b22bc4367ecc7b4e0 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 10:24:25 -0400 Subject: 420857 - Part 3: Report the position of opening brace for missing brace error in object literal. --- js/src/frontend/Parser.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index ef352ec35..7dc3cdf8f 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -9524,6 +9524,8 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); + uint32_t openedPos = pos().begin; + Node literal = handler.newObjectLiteral(pos().begin); if (!literal) return null(); @@ -9686,7 +9688,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* if (tt == TOK_RC) break; if (tt != TOK_COMMA) { - error(JSMSG_CURLY_AFTER_LIST); + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos); return null(); } } -- cgit v1.2.3 From e93a48121a1a14ac228bebe362e7c27b1aadd4d5 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 10:26:41 -0400 Subject: 420857 - Part 4: Report the position of opening bracket for missing bracket error in array literal. --- js/src/frontend/Parser.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 7dc3cdf8f..3154e41d5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -9289,7 +9289,9 @@ Parser::arrayInitializer(YieldHandling yieldHandling, PossibleErro } } - MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST); + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier, + reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, + JSMSG_BRACKET_OPENED, begin)); } handler.setEndPosition(literal, pos().end); return literal; -- cgit v1.2.3 From b02f52f8a7cfedb92b1f6cf9a09c0a220b68b2f4 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 12:54:05 -0400 Subject: 1303703 - Part 1: Separate binding pattern parsing from object/array literal parsing. --- js/src/frontend/Parser.cpp | 398 +++++++++++++++++++++++++++++++++++---------- js/src/frontend/Parser.h | 30 ++-- 2 files changed, 331 insertions(+), 97 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 3154e41d5..85f7ee846 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4243,7 +4243,7 @@ Parser::PossibleError::transferErrorsTo(PossibleError* other) template <> bool -Parser::checkDestructuringName(ParseNode* expr, Maybe maybeDecl) +Parser::checkDestructuringAssignmentName(ParseNode* expr) { MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr)); @@ -4255,24 +4255,8 @@ Parser::checkDestructuringName(ParseNode* expr, Maybepn_pos.begin, JSMSG_NO_VARIABLE_NAME); - return false; - } - - RootedPropertyName name(context, expr->name()); - // `yield` is already checked, so pass YieldIsName to skip that check. - if (!checkBindingIdentifier(name, expr->pn_pos.begin, YieldIsName)) - return false; - return noteDeclaredName(name, *maybeDecl, expr->pn_pos); - } - - // Otherwise this is an expression in destructuring outside a declaration. + // The expression must be a simple assignment target, i.e. either a name + // or a property accessor. if (handler.isNameAnyParentheses(expr)) { if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(expr, context)) { if (!strictModeErrorAt(expr->pn_pos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) @@ -4291,14 +4275,12 @@ Parser::checkDestructuringName(ParseNode* expr, Maybe bool -Parser::checkDestructuringPattern(ParseNode* pattern, - Maybe maybeDecl, - PossibleError* possibleError /* = nullptr */); +Parser::checkDestructuringAssignmentPattern(ParseNode* pattern, + PossibleError* possibleError /* = nullptr */); template <> bool -Parser::checkDestructuringObject(ParseNode* objectPattern, - Maybe maybeDecl) +Parser::checkDestructuringAssignmentObject(ParseNode* objectPattern) { MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT)); @@ -4319,10 +4301,10 @@ Parser::checkDestructuringObject(ParseNode* objectPattern, target = target->pn_left; if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (!checkDestructuringPattern(target, maybeDecl)) + if (!checkDestructuringAssignmentPattern(target)) return false; } else { - if (!checkDestructuringName(target, maybeDecl)) + if (!checkDestructuringAssignmentName(target)) return false; } } @@ -4332,8 +4314,7 @@ Parser::checkDestructuringObject(ParseNode* objectPattern, template <> bool -Parser::checkDestructuringArray(ParseNode* arrayPattern, - Maybe maybeDecl) +Parser::checkDestructuringAssignmentArray(ParseNode* arrayPattern) { MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY)); @@ -4355,10 +4336,10 @@ Parser::checkDestructuringArray(ParseNode* arrayPattern, } if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (!checkDestructuringPattern(target, maybeDecl)) + if (!checkDestructuringAssignmentPattern(target)) return false; } else { - if (!checkDestructuringName(target, maybeDecl)) + if (!checkDestructuringAssignmentName(target)) return false; } } @@ -4379,32 +4360,28 @@ Parser::checkDestructuringArray(ParseNode* arrayPattern, * these cases, the patterns' property value positions must be * simple names; the destructuring defines them as new variables. * - * In both cases, other code parses the pattern as an arbitrary - * primaryExpr, and then, here in checkDestructuringPattern, verify - * that the tree is a valid AssignmentPattern or BindingPattern. + * In the first case, other code parses the pattern as an arbitrary + * primaryExpr, and then, here in checkDestructuringAssignmentPattern, verify + * that the tree is a valid AssignmentPattern. * * In assignment-like contexts, we parse the pattern with - * pc->inDestructuringDecl clear, so the lvalue expressions in the - * pattern are parsed normally. primaryExpr links variable references - * into the appropriate use chains; creates placeholder definitions; - * and so on. checkDestructuringPattern won't bind any new names and - * we specialize lvalues as appropriate. + * pc->inDestructuringDecl clear, so the lvalue expressions in the pattern are + * parsed normally. identifierReference() links variable references into the + * appropriate use chains; creates placeholder definitions; and so on. + * checkDestructuringAssignmentPattern won't bind any new names and we + * specialize lvalues as appropriate. * - * In declaration-like contexts, the normal variable reference - * processing would just be an obstruction, because we're going to - * define the names that appear in the property value positions as new - * variables anyway. In this case, we parse the pattern with - * pc->inDestructuringDecl set, which directs primaryExpr to leave - * whatever name nodes it creates unconnected. Then, here in - * checkDestructuringPattern, we require the pattern's property value - * positions to be simple names, and define them as appropriate to the - * context. + * In declaration-like contexts, the normal variable reference processing + * would just be an obstruction, because we're going to define the names that + * appear in the property value positions as new variables anyway. In this + * case, we parse the pattern in destructuringDeclaration() with + * pc->inDestructuringDecl set, which directs identifierReference() to leave + * whatever name nodes it creates unconnected. */ template <> bool -Parser::checkDestructuringPattern(ParseNode* pattern, - Maybe maybeDecl, - PossibleError* possibleError /* = nullptr */) +Parser::checkDestructuringAssignmentPattern(ParseNode* pattern, + PossibleError* possibleError /* = nullptr */) { if (pattern->isKind(PNK_ARRAYCOMP)) { errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE); @@ -4412,8 +4389,8 @@ Parser::checkDestructuringPattern(ParseNode* pattern, } bool isDestructuring = pattern->isKind(PNK_ARRAY) - ? checkDestructuringArray(pattern, maybeDecl) - : checkDestructuringObject(pattern, maybeDecl); + ? checkDestructuringAssignmentArray(pattern) + : checkDestructuringAssignmentObject(pattern); // Report any pending destructuring error. if (isDestructuring && possibleError && !possibleError->checkForDestructuringError()) @@ -4422,11 +4399,275 @@ Parser::checkDestructuringPattern(ParseNode* pattern, return isDestructuring; } +class AutoClearInDestructuringDecl +{ + ParseContext* pc_; + Maybe saved_; + + public: + explicit AutoClearInDestructuringDecl(ParseContext* pc) + : pc_(pc), + saved_(pc->inDestructuringDecl) + { + pc->inDestructuringDecl = Nothing(); + if (saved_ && *saved_ == DeclarationKind::FormalParameter) + pc->functionBox()->hasParameterExprs = true; + } + + ~AutoClearInDestructuringDecl() { + pc_->inDestructuringDecl = saved_; + } +}; + +template +typename ParseHandler::Node +Parser::bindingInitializer(Node lhs, YieldHandling yieldHandling) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); + + Node rhs; + { + AutoClearInDestructuringDecl autoClear(pc); + rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); + } + + handler.checkAndSetIsDirectRHSAnonFunction(rhs); + + Node assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + if (!assign) + return null(); + + if (foldConstants && !FoldConstants(context, &assign, this)) + return null(); + + return assign; +} + +template +typename ParseHandler::Node +Parser::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling) +{ + Rooted name(context, bindingIdentifier(yieldHandling)); + if (!name) + return null(); + + Node binding = newName(name); + if (!binding || !noteDeclaredName(name, kind, pos())) + return null(); + + return binding; +} + +template +typename ParseHandler::Node +Parser::bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling, + TokenKind tt) +{ + if (tt == TOK_LB) + return arrayBindingPattern(kind, yieldHandling); + + if (tt == TOK_LC) + return objectBindingPattern(kind, yieldHandling); + + if (!TokenKindIsPossibleIdentifierName(tt)) { + error(JSMSG_NO_VARIABLE_NAME); + return null(); + } + + return bindingIdentifier(kind, yieldHandling); +} + +template +typename ParseHandler::Node +Parser::objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); + + JS_CHECK_RECURSION(context, return null()); + + uint32_t begin = pos().begin; + Node literal = handler.newObjectLiteral(begin); + if (!literal) + return null(); + + RootedAtom propAtom(context); + for (;;) { + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + if (tt == TOK_RC) + break; + + TokenPos namePos = pos(); + + tokenStream.ungetToken(); + + PropertyType propType; + Node propName = propertyName(yieldHandling, literal, &propType, &propAtom); + if (!propName) + return null(); + + if (propType == PropertyType::Normal) { + // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. + + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!binding) + return null(); + + bool hasInitializer; + if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) + return null(); + + Node bindingExpr = hasInitializer + ? bindingInitializer(binding, yieldHandling) + : binding; + if (!bindingExpr) + return null(); + + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else if (propType == PropertyType::Shorthand) { + // Handle e.g., |var {x, y} = o| as destructuring shorthand + // for |var {x: x, y: y} = o|. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); + + if (!handler.addShorthand(literal, propName, binding)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + // Handle e.g., |var {x=1, y=2} = o| as destructuring shorthand + // with default values. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); + + tokenStream.consumeKnownToken(TOK_ASSIGN); + + Node bindingExpr = bindingInitializer(binding, yieldHandling); + if (!bindingExpr) + return null(); + + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else { + errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); + return null(); + } + + if (!tokenStream.getToken(&tt)) + return null(); + if (tt == TOK_RC) + break; + if (tt != TOK_COMMA) { + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin); + return null(); + } + } + + handler.setEndPosition(literal, pos().end); + return literal; +} + +template +typename ParseHandler::Node +Parser::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling) +{ + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB)); + + JS_CHECK_RECURSION(context, return null()); + + uint32_t begin = pos().begin; + Node literal = handler.newArrayLiteral(begin); + if (!literal) + 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); + return null(); + } + + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + if (tt == TOK_RB) { + tokenStream.ungetToken(); + break; + } + + if (tt == TOK_COMMA) { + if (!handler.addElision(literal, pos())) + return null(); + } else if (tt == TOK_TRIPLEDOT) { + uint32_t begin = pos().begin; + + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); + + Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!inner) + return null(); + + if (!handler.addSpreadElement(literal, begin, inner)) + return null(); + } else { + Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!binding) + return null(); + + bool hasInitializer; + if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) + return null(); + + Node element = hasInitializer ? bindingInitializer(binding, yieldHandling) : binding; + if (!element) + return null(); + + 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) { + error(JSMSG_REST_WITH_COMMA); + return null(); + } + } + } + + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier, + reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, + JSMSG_BRACKET_OPENED, begin)); + + handler.setEndPosition(literal, pos().end); + return literal; +} + template <> bool -Parser::checkDestructuringPattern(Node pattern, - Maybe maybeDecl, - PossibleError* possibleError /* = nullptr */) +Parser::checkDestructuringAssignmentPattern(Node pattern, + PossibleError* possibleError /* = nullptr */) { return abortIfSyntaxParser(); } @@ -4439,17 +4680,16 @@ Parser::destructuringDeclaration(DeclarationKind kind, YieldHandli MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC); - PossibleError possibleError(*this); Node pattern; { pc->inDestructuringDecl = Some(kind); - pattern = primaryExpr(yieldHandling, TripledotProhibited, tt, &possibleError); + if (tt == TOK_LB) + pattern = arrayBindingPattern(kind, yieldHandling); + else + pattern = objectBindingPattern(kind, yieldHandling); pc->inDestructuringDecl = Nothing(); } - if (!pattern || !checkDestructuringPattern(pattern, Some(kind), &possibleError)) - return null(); - return pattern; } @@ -4564,6 +4804,12 @@ Parser::declarationPattern(Node decl, DeclarationKind declKind, To // 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); } @@ -5975,7 +6221,7 @@ Parser::forHeadStart(YieldHandling yieldHandling, // Verify the left-hand side expression doesn't have a forbidden form. if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) { - if (!checkDestructuringPattern(*forInitialPart, Nothing(), &possibleError)) + if (!checkDestructuringAssignmentPattern(*forInitialPart, &possibleError)) return false; } else if (handler.isNameAnyParentheses(*forInitialPart)) { const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context); @@ -7920,26 +8166,6 @@ Parser::condExpr1(InHandling inHandling, YieldHandling yieldHandli return handler.newConditional(condition, thenExpr, elseExpr); } -class AutoClearInDestructuringDecl -{ - ParseContext* pc_; - Maybe saved_; - - public: - explicit AutoClearInDestructuringDecl(ParseContext* pc) - : pc_(pc), - saved_(pc->inDestructuringDecl) - { - pc->inDestructuringDecl = Nothing(); - if (saved_ && *saved_ == DeclarationKind::FormalParameter) - pc->functionBox()->hasParameterExprs = true; - } - - ~AutoClearInDestructuringDecl() { - pc_->inDestructuringDecl = saved_; - } -}; - template typename ParseHandler::Node Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandling, @@ -8170,7 +8396,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl return null(); } - if (!checkDestructuringPattern(lhs, Nothing(), &possibleErrorInner)) + if (!checkDestructuringAssignmentPattern(lhs, &possibleErrorInner)) return null(); } else if (handler.isNameAnyParentheses(lhs)) { if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { @@ -9505,7 +9731,7 @@ Parser::computedPropertyName(YieldHandling yieldHandling, Node lit // Turn off the inDestructuringDecl flag when parsing computed property // names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName() // should be called on x and y, but not on z. See the comment on - // Parser<>::checkDestructuringPattern() for details. + // Parser<>::checkDestructuringAssignmentPattern() for details. AutoClearInDestructuringDecl autoClear(pc); assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!assignNode) @@ -9593,8 +9819,8 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* } } else if (propType == PropertyType::Shorthand) { /* - * Support, e.g., |var {x, y} = o| as destructuring shorthand - * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer + * Support, e.g., |({x, y} = o)| as destructuring shorthand + * for |({x: x, y: y} = o)|, and |var o = {x, y}| as initializer * shorthand for |var o = {x: x, y: y}|. */ Rooted name(context, identifierReference(yieldHandling)); @@ -9609,7 +9835,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* return null(); } else if (propType == PropertyType::CoverInitializedName) { /* - * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand + * Support, e.g., |({x=1, y=2} = o)| as destructuring shorthand * with default values, as per ES6 12.14.5 */ Rooted name(context, identifierReference(yieldHandling)); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b1d3bdee0..9b6387c9f 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1498,17 +1498,25 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError); - // Top-level entrypoint into destructuring pattern checking/name-analyzing. - bool checkDestructuringPattern(Node pattern, mozilla::Maybe maybeDecl, - PossibleError* possibleError = nullptr); - - // Recursive methods for checking/name-analyzing subcomponents of a - // destructuring pattern. The array/object methods *must* be passed arrays - // or objects. The name method may be passed anything but will report an - // error if not passed a name. - bool checkDestructuringArray(Node arrayPattern, mozilla::Maybe maybeDecl); - bool checkDestructuringObject(Node objectPattern, mozilla::Maybe maybeDecl); - bool checkDestructuringName(Node expr, mozilla::Maybe maybeDecl); + Node bindingInitializer(Node lhs, YieldHandling yieldHandling); + Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling); + Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling, + TokenKind tt); + Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling); + Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling); + + // Top-level entrypoint into destructuring assignment pattern checking and + // name-analyzing. + bool checkDestructuringAssignmentPattern(Node pattern, + PossibleError* possibleError = nullptr); + + // Recursive methods for checking/name-analyzing subcomponents of an + // destructuring assignment pattern. The array/object methods *must* be + // passed arrays or objects. The name method may be passed anything but + // will report an error if not passed a name. + bool checkDestructuringAssignmentArray(Node arrayPattern); + bool checkDestructuringAssignmentObject(Node objectPattern); + bool checkDestructuringAssignmentName(Node expr); Node newNumber(const Token& tok) { return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos); -- cgit v1.2.3 From 55c897db3c504c10650b94d97bb13ed79b0e23c6 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 13:05:27 -0400 Subject: 1303703 - Part 2: Clean-up bits of destructuring parsing which are no longer needed. --- js/src/frontend/Parser.cpp | 151 +++++++++++++++------------------------------ js/src/frontend/Parser.h | 19 ++---- 2 files changed, 54 insertions(+), 116 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 85f7ee846..16eb7d85c 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4363,20 +4363,6 @@ Parser::checkDestructuringAssignmentArray(ParseNode* arrayPatt * In the first case, other code parses the pattern as an arbitrary * primaryExpr, and then, here in checkDestructuringAssignmentPattern, verify * that the tree is a valid AssignmentPattern. - * - * In assignment-like contexts, we parse the pattern with - * pc->inDestructuringDecl clear, so the lvalue expressions in the pattern are - * parsed normally. identifierReference() links variable references into the - * appropriate use chains; creates placeholder definitions; and so on. - * checkDestructuringAssignmentPattern won't bind any new names and we - * specialize lvalues as appropriate. - * - * In declaration-like contexts, the normal variable reference processing - * would just be an obstruction, because we're going to define the names that - * appear in the property value positions as new variables anyway. In this - * case, we parse the pattern in destructuringDeclaration() with - * pc->inDestructuringDecl set, which directs identifierReference() to leave - * whatever name nodes it creates unconnected. */ template <> bool @@ -4399,39 +4385,19 @@ Parser::checkDestructuringAssignmentPattern(ParseNode* pattern return isDestructuring; } -class AutoClearInDestructuringDecl -{ - ParseContext* pc_; - Maybe saved_; - - public: - explicit AutoClearInDestructuringDecl(ParseContext* pc) - : pc_(pc), - saved_(pc->inDestructuringDecl) - { - pc->inDestructuringDecl = Nothing(); - if (saved_ && *saved_ == DeclarationKind::FormalParameter) - pc->functionBox()->hasParameterExprs = true; - } - - ~AutoClearInDestructuringDecl() { - pc_->inDestructuringDecl = saved_; - } -}; - template typename ParseHandler::Node -Parser::bindingInitializer(Node lhs, YieldHandling yieldHandling) +Parser::bindingInitializer(Node lhs, DeclarationKind kind, + YieldHandling yieldHandling) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_ASSIGN)); - Node rhs; - { - AutoClearInDestructuringDecl autoClear(pc); - rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); - if (!rhs) - return null(); - } + if (kind == DeclarationKind::FormalParameter) + pc->functionBox()->hasParameterExprs = true; + + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); handler.checkAndSetIsDirectRHSAnonFunction(rhs); @@ -4449,7 +4415,7 @@ template typename ParseHandler::Node Parser::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling) { - Rooted name(context, bindingIdentifier(yieldHandling)); + RootedPropertyName name(context, bindingIdentifier(yieldHandling)); if (!name) return null(); @@ -4492,6 +4458,7 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y if (!literal) return null(); + Maybe declKind = Some(kind); RootedAtom propAtom(context); for (;;) { TokenKind tt; @@ -4505,7 +4472,7 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y tokenStream.ungetToken(); PropertyType propType; - Node propName = propertyName(yieldHandling, literal, &propType, &propAtom); + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); if (!propName) return null(); @@ -4524,7 +4491,7 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y return null(); Node bindingExpr = hasInitializer - ? bindingInitializer(binding, yieldHandling) + ? bindingInitializer(binding, kind, yieldHandling) : binding; if (!bindingExpr) return null(); @@ -4553,7 +4520,7 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y tokenStream.consumeKnownToken(TOK_ASSIGN); - Node bindingExpr = bindingInitializer(binding, yieldHandling); + Node bindingExpr = bindingInitializer(binding, kind, yieldHandling); if (!bindingExpr) return null(); @@ -4633,7 +4600,9 @@ Parser::arrayBindingPattern(DeclarationKind kind, YieldHandling yi if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) return null(); - Node element = hasInitializer ? bindingInitializer(binding, yieldHandling) : binding; + Node element = hasInitializer + ? bindingInitializer(binding, kind, yieldHandling) + : binding; if (!element) return null(); @@ -4680,17 +4649,9 @@ Parser::destructuringDeclaration(DeclarationKind kind, YieldHandli MOZ_ASSERT(tokenStream.isCurrentTokenType(tt)); MOZ_ASSERT(tt == TOK_LB || tt == TOK_LC); - Node pattern; - { - pc->inDestructuringDecl = Some(kind); - if (tt == TOK_LB) - pattern = arrayBindingPattern(kind, yieldHandling); - else - pattern = objectBindingPattern(kind, yieldHandling); - pc->inDestructuringDecl = Nothing(); - } - - return pattern; + return tt == TOK_LB + ? arrayBindingPattern(kind, yieldHandling) + : objectBindingPattern(kind, yieldHandling); } template @@ -7066,14 +7027,10 @@ Parser::tryStatement(YieldHandling yieldHandling) return null(); } - RootedPropertyName param(context, bindingIdentifier(yieldHandling)); - if (!param) - return null(); - catchName = newName(param); + catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter, + yieldHandling); if (!catchName) return null(); - if (!noteDeclaredName(param, DeclarationKind::SimpleCatchParameter, pos())) - return null(); break; } } @@ -7301,6 +7258,7 @@ Parser::classDefinition(YieldHandling yieldHandling, if (!classMethods) return null(); + Maybe declKind = Nothing(); for (;;) { TokenKind tt; if (!tokenStream.getToken(&tt)) @@ -7335,7 +7293,7 @@ Parser::classDefinition(YieldHandling yieldHandling, return null(); PropertyType propType; - Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom); + Node propName = propertyName(yieldHandling, declKind, classMethods, &propType, &propAtom); if (!propName) return null(); @@ -8419,13 +8377,9 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!possibleErrorInner.checkForExpressionError()) return null(); - Node rhs; - { - AutoClearInDestructuringDecl autoClear(pc); - rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); - if (!rhs) - return null(); - } + Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); if (kind == PNK_ASSIGN) handler.checkAndSetIsDirectRHSAnonFunction(rhs); @@ -9370,7 +9324,7 @@ Parser::identifierReference(Handle name) if (!pn) return null(); - if (!pc->inDestructuringDecl && !noteUsedName(name)) + if (!noteUsedName(name)) return null(); return pn; @@ -9533,7 +9487,8 @@ DoubleToAtom(ExclusiveContext* cx, double value) template typename ParseHandler::Node -Parser::propertyName(YieldHandling yieldHandling, Node propList, +Parser::propertyName(YieldHandling yieldHandling, + const Maybe& maybeDecl, Node propList, PropertyType* propType, MutableHandleAtom propAtom) { TokenKind ltok; @@ -9594,7 +9549,7 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, break; case TOK_LB: - propName = computedPropertyName(yieldHandling, propList); + propName = computedPropertyName(yieldHandling, maybeDecl, propList); if (!propName) return null(); break; @@ -9652,7 +9607,7 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, if (tt == TOK_LB) { tokenStream.consumeKnownToken(TOK_LB); - return computedPropertyName(yieldHandling, propList); + return computedPropertyName(yieldHandling, maybeDecl, propList); } // Not an accessor property after all. @@ -9722,28 +9677,25 @@ Parser::propertyName(YieldHandling yieldHandling, Node propList, template typename ParseHandler::Node -Parser::computedPropertyName(YieldHandling yieldHandling, Node literal) +Parser::computedPropertyName(YieldHandling yieldHandling, + const Maybe& maybeDecl, + Node literal) { uint32_t begin = pos().begin; - Node assignNode; - { - // Turn off the inDestructuringDecl flag when parsing computed property - // names. In short, when parsing 'let {[x + y]: z} = obj;', noteUsedName() - // should be called on x and y, but not on z. See the comment on - // Parser<>::checkDestructuringAssignmentPattern() for details. - AutoClearInDestructuringDecl autoClear(pc); - assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited); - if (!assignNode) - return null(); + if (maybeDecl) { + if (*maybeDecl == DeclarationKind::FormalParameter) + pc->functionBox()->hasParameterExprs = true; + } else { + handler.setListFlag(literal, PNX_NONCONST); } - MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR); - Node propname = handler.newComputedName(assignNode, begin, pos().end); - if (!propname) + Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!assignNode) return null(); - handler.setListFlag(literal, PNX_NONCONST); - return propname; + + MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR); + return handler.newComputedName(assignNode, begin, pos().end); } template @@ -9760,6 +9712,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* bool seenPrototypeMutation = false; bool seenCoverInitializedName = false; + Maybe declKind = Nothing(); RootedAtom propAtom(context); for (;;) { TokenKind tt; @@ -9773,7 +9726,7 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* tokenStream.ungetToken(); PropertyType propType; - Node propName = propertyName(yieldHandling, literal, &propType, &propAtom); + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); if (!propName) return null(); @@ -9867,15 +9820,9 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } - Node rhs; - { - // Clearing `inDestructuringDecl` allows name use to be noted - // in Parser::identifierReference. See bug 1255167. - AutoClearInDestructuringDecl autoClear(pc); - rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); - if (!rhs) - return null(); - } + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) + return null(); handler.checkAndSetIsDirectRHSAnonFunction(rhs); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 9b6387c9f..8bd691fc4 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -340,17 +340,6 @@ class ParseContext : public Nestable // pointer may be nullptr. Directives* newDirectives; - // Set when parsing a declaration-like destructuring pattern. This flag - // causes PrimaryExpr to create PN_NAME parse nodes for variable references - // which are not hooked into any definition's use chain, added to any tree - // context's AtomList, etc. etc. checkDestructuring will do that work - // later. - // - // The comments atop checkDestructuring explain the distinction between - // assignment-like and declaration-like destructuring patterns, and why - // they need to be treated differently. - mozilla::Maybe inDestructuringDecl; - // Set when parsing a function and it has 'return ;' bool funHasReturnExpr; @@ -1490,15 +1479,17 @@ class Parser final : public ParserBase, private JS::AutoGCRooter mozilla::Maybe newLexicalScopeData(ParseContext::Scope& scope); Node finishLexicalScope(ParseContext::Scope& scope, Node body); - Node propertyName(YieldHandling yieldHandling, Node propList, + Node propertyName(YieldHandling yieldHandling, + const mozilla::Maybe& maybeDecl, Node propList, PropertyType* propType, MutableHandleAtom propAtom); - Node computedPropertyName(YieldHandling yieldHandling, Node literal); + Node computedPropertyName(YieldHandling yieldHandling, + const mozilla::Maybe& maybeDecl, Node literal); Node arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError); Node newRegExp(); Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError); - Node bindingInitializer(Node lhs, YieldHandling yieldHandling); + Node bindingInitializer(Node lhs, DeclarationKind kind, YieldHandling yieldHandling); Node bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling); Node bindingIdentifierOrPattern(DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt); -- cgit v1.2.3 From a4b91b5a8faf9dc09db252c89921775910f1c233 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 13:39:17 -0400 Subject: 1303703 - Part 3: Syntax parse destructuring assignment patterns. --- js/src/frontend/FullParseHandler.h | 6 +- js/src/frontend/Parser.cpp | 333 +++++++++++++++++------------------ js/src/frontend/Parser.h | 53 +++--- js/src/frontend/SyntaxParseHandler.h | 6 +- 4 files changed, 200 insertions(+), 198 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 7ddd8d306..c7a7e0843 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -878,6 +878,10 @@ class FullParseHandler return node->isKind(PNK_NAME); } + bool isArgumentsAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + return node->isKind(PNK_NAME) && node->pn_atom == cx->names().arguments; + } + bool isEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval; } @@ -888,7 +892,7 @@ class FullParseHandler if (isEvalAnyParentheses(node, cx)) return js_eval_str; - if (node->pn_atom == cx->names().arguments) + if (isArgumentsAnyParentheses(node, cx)) return js_arguments_str; return nullptr; } diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 16eb7d85c..c1e165db5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -689,6 +689,19 @@ ParserBase::extraWarning(unsigned errorNumber, ...) return result; } +bool +ParserBase::extraWarningAt(uint32_t offset, unsigned errorNumber, ...) +{ + va_list args; + va_start(args, errorNumber); + + bool result = + tokenStream.reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, args); + + va_end(args); + return result; +} + bool ParserBase::strictModeError(unsigned errorNumber, ...) { @@ -743,14 +756,6 @@ ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumb return result; } -template <> -bool -Parser::abortIfSyntaxParser() -{ - handler.disableSyntaxParser(); - return true; -} - template <> bool Parser::abortIfSyntaxParser() @@ -4128,8 +4133,17 @@ Parser::PossibleError::error(ErrorKind kind) { if (kind == ErrorKind::Expression) return exprError_; - MOZ_ASSERT(kind == ErrorKind::Destructuring); - return destructuringError_; + if (kind == ErrorKind::Destructuring) + return destructuringError_; + MOZ_ASSERT(kind == ErrorKind::DestructuringWarning); + return destructuringWarning_; +} + +template +bool +Parser::PossibleError::hasPendingDestructuringError() +{ + return hasError(ErrorKind::Destructuring); } template @@ -4171,6 +4185,14 @@ Parser::PossibleError::setPendingDestructuringErrorAt(const TokenP setPending(ErrorKind::Destructuring, pos, errorNumber); } +template +void +Parser::PossibleError::setPendingDestructuringWarningAt(const TokenPos& pos, + unsigned errorNumber) +{ + setPending(ErrorKind::DestructuringWarning, pos, errorNumber); +} + template void Parser::PossibleError::setPendingExpressionErrorAt(const TokenPos& pos, @@ -4193,23 +4215,36 @@ Parser::PossibleError::checkForError(ErrorKind kind) template bool -Parser::PossibleError::checkForDestructuringError() +Parser::PossibleError::checkForWarning(ErrorKind kind) +{ + if (!hasError(kind)) + return true; + + Error& err = error(kind); + return parser_.extraWarningAt(err.offset_, err.errorNumber_); +} + +template +bool +Parser::PossibleError::checkForDestructuringErrorOrWarning() { // Clear pending expression error, because we're definitely not in an // expression context. setResolved(ErrorKind::Expression); - // Report any pending destructuring error. - return checkForError(ErrorKind::Destructuring); + // Report any pending destructuring error or warning. + return checkForError(ErrorKind::Destructuring) && + checkForWarning(ErrorKind::DestructuringWarning); } template bool Parser::PossibleError::checkForExpressionError() { - // Clear pending destructuring error, because we're definitely not in a - // destructuring context. + // Clear pending destructuring error or warning, because we're definitely + // not in a destructuring context. setResolved(ErrorKind::Destructuring); + setResolved(ErrorKind::DestructuringWarning); // Report any pending expression error. return checkForError(ErrorKind::Expression); @@ -4241,150 +4276,6 @@ Parser::PossibleError::transferErrorsTo(PossibleError* other) transferErrorTo(ErrorKind::Expression, other); } -template <> -bool -Parser::checkDestructuringAssignmentName(ParseNode* expr) -{ - MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr)); - - // Parentheses are forbidden around destructuring *patterns* (but allowed - // around names). Use our nicer error message for parenthesized, nested - // patterns. - if (handler.isParenthesizedDestructuringPattern(expr)) { - errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_PARENS); - return false; - } - - // The expression must be a simple assignment target, i.e. either a name - // or a property accessor. - if (handler.isNameAnyParentheses(expr)) { - if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(expr, context)) { - if (!strictModeErrorAt(expr->pn_pos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) - return false; - } - - return true; - } - - if (handler.isPropertyAccess(expr)) - return true; - - errorAt(expr->pn_pos.begin, JSMSG_BAD_DESTRUCT_TARGET); - return false; -} - -template <> -bool -Parser::checkDestructuringAssignmentPattern(ParseNode* pattern, - PossibleError* possibleError /* = nullptr */); - -template <> -bool -Parser::checkDestructuringAssignmentObject(ParseNode* objectPattern) -{ - MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT)); - - for (ParseNode* member = objectPattern->pn_head; member; member = member->pn_next) { - ParseNode* target; - if (member->isKind(PNK_MUTATEPROTO)) { - target = member->pn_kid; - } else { - MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND)); - MOZ_ASSERT_IF(member->isKind(PNK_SHORTHAND), - member->pn_left->isKind(PNK_OBJECT_PROPERTY_NAME) && - member->pn_right->isKind(PNK_NAME) && - member->pn_left->pn_atom == member->pn_right->pn_atom); - - target = member->pn_right; - } - if (handler.isUnparenthesizedAssignment(target)) - target = target->pn_left; - - if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (!checkDestructuringAssignmentPattern(target)) - return false; - } else { - if (!checkDestructuringAssignmentName(target)) - return false; - } - } - - return true; -} - -template <> -bool -Parser::checkDestructuringAssignmentArray(ParseNode* arrayPattern) -{ - MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY)); - - for (ParseNode* element = arrayPattern->pn_head; element; element = element->pn_next) { - if (element->isKind(PNK_ELISION)) - continue; - - ParseNode* target; - if (element->isKind(PNK_SPREAD)) { - if (element->pn_next) { - errorAt(element->pn_next->pn_pos.begin, JSMSG_PARAMETER_AFTER_REST); - return false; - } - target = element->pn_kid; - } else if (handler.isUnparenthesizedAssignment(element)) { - target = element->pn_left; - } else { - target = element; - } - - if (handler.isUnparenthesizedDestructuringPattern(target)) { - if (!checkDestructuringAssignmentPattern(target)) - return false; - } else { - if (!checkDestructuringAssignmentName(target)) - return false; - } - } - - return true; -} - -/* - * Destructuring patterns can appear in two kinds of contexts: - * - * - assignment-like: assignment expressions and |for| loop heads. In - * these cases, the patterns' property value positions can be - * arbitrary lvalue expressions; the destructuring is just a fancy - * assignment. - * - * - binding-like: |var| and |let| declarations, functions' formal - * parameter lists, |catch| clauses, and comprehension tails. In - * these cases, the patterns' property value positions must be - * simple names; the destructuring defines them as new variables. - * - * In the first case, other code parses the pattern as an arbitrary - * primaryExpr, and then, here in checkDestructuringAssignmentPattern, verify - * that the tree is a valid AssignmentPattern. - */ -template <> -bool -Parser::checkDestructuringAssignmentPattern(ParseNode* pattern, - PossibleError* possibleError /* = nullptr */) -{ - if (pattern->isKind(PNK_ARRAYCOMP)) { - errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE); - return false; - } - - bool isDestructuring = pattern->isKind(PNK_ARRAY) - ? checkDestructuringAssignmentArray(pattern) - : checkDestructuringAssignmentObject(pattern); - - // Report any pending destructuring error. - if (isDestructuring && possibleError && !possibleError->checkForDestructuringError()) - return false; - - return isDestructuring; -} - template typename ParseHandler::Node Parser::bindingInitializer(Node lhs, DeclarationKind kind, @@ -5153,14 +5044,6 @@ Parser::namedImportsOrNamespaceImport(TokenKind tt, Node impor return true; } -template<> -bool -Parser::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet) -{ - MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); - return false; -} - template<> ParseNode* Parser::importDeclaration() @@ -6182,7 +6065,7 @@ Parser::forHeadStart(YieldHandling yieldHandling, // Verify the left-hand side expression doesn't have a forbidden form. if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) { - if (!checkDestructuringAssignmentPattern(*forInitialPart, &possibleError)) + if (!possibleError.checkForDestructuringErrorOrWarning()) return false; } else if (handler.isNameAnyParentheses(*forInitialPart)) { const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context); @@ -8148,7 +8031,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (!tokenStream.getToken(&tt, TokenStream::Operand)) return null(); - uint32_t exprOffset = pos().begin; + TokenPos exprPos = pos(); bool endsExpr; @@ -8354,12 +8237,12 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl return null(); } - if (!checkDestructuringAssignmentPattern(lhs, &possibleErrorInner)) + if (!possibleErrorInner.checkForDestructuringErrorOrWarning()) return null(); } else if (handler.isNameAnyParentheses(lhs)) { if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { // |chars| is "arguments" or "eval" here. - if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) + if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) return null(); } @@ -8367,10 +8250,13 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl } else if (handler.isPropertyAccess(lhs)) { // Permitted: no additional testing/fixup needed. } else if (handler.isFunctionCall(lhs)) { - if (!strictModeErrorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS)) + if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS)) return null(); + + if (possibleError) + possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); } else { - errorAt(exprOffset, JSMSG_BAD_LEFTSIDE_OF_ASS); + errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS); return null(); } @@ -9390,6 +9276,74 @@ Parser::newRegExp() return handler.newRegExp(reobj, pos(), *this); } +template +void +Parser::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos, + PossibleError* possibleError) +{ + // Return early if a pending destructuring error is already present. + if (possibleError->hasPendingDestructuringError()) + return; + + if (pc->sc()->needStrictChecks()) { + if (handler.isArgumentsAnyParentheses(expr, context)) { + if (pc->sc()->strict()) { + possibleError->setPendingDestructuringErrorAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS); + } else { + possibleError->setPendingDestructuringWarningAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS); + } + return; + } + + if (handler.isEvalAnyParentheses(expr, context)) { + if (pc->sc()->strict()) { + possibleError->setPendingDestructuringErrorAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_EVAL); + } else { + possibleError->setPendingDestructuringWarningAt(exprPos, + JSMSG_BAD_STRICT_ASSIGN_EVAL); + } + return; + } + } + + // The expression must be either a simple assignment target, i.e. a name + // or a property accessor, or a nested destructuring pattern. + if (!handler.isUnparenthesizedDestructuringPattern(expr) && + !handler.isNameAnyParentheses(expr) && + !handler.isPropertyAccess(expr)) + { + // Parentheses are forbidden around destructuring *patterns* (but + // allowed around names). Use our nicer error message for + // parenthesized, nested patterns. + if (handler.isParenthesizedDestructuringPattern(expr)) + possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS); + else + possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); + } +} + +template +void +Parser::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos, + PossibleError* possibleError) +{ + // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834 + // 12.15.5 Destructuring Assignment + // + // AssignmentElement[Yield, Await]: + // DestructuringAssignmentTarget[?Yield, ?Await] + // DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await] + + // If |expr| is an assignment element with an initializer expression, its + // destructuring assignment target was already validated in assignExpr(). + // Otherwise we need to check that |expr| is a valid destructuring target. + if (!handler.isUnparenthesizedAssignment(expr)) + checkDestructuringAssignmentTarget(expr, exprPos, possibleError); +} + template typename ParseHandler::Node Parser::arrayInitializer(YieldHandling yieldHandling, PossibleError* possibleError) @@ -9439,17 +9393,29 @@ Parser::arrayInitializer(YieldHandling yieldHandling, PossibleErro } else if (tt == TOK_TRIPLEDOT) { tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand); uint32_t begin = pos().begin; + + TokenPos innerPos; + if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand)) + return null(); + Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!inner) return null(); + if (possibleError) + checkDestructuringAssignmentTarget(inner, innerPos, possibleError); if (!handler.addSpreadElement(literal, begin, inner)) return null(); } else { + TokenPos elementPos; + if (!tokenStream.peekTokenPos(&elementPos, TokenStream::Operand)) + return null(); Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!element) return null(); + if (possibleError) + checkDestructuringAssignmentElement(element, elementPos, possibleError); if (foldConstants && !FoldConstants(context, &element, this)) return null(); handler.addArrayElement(literal, element); @@ -9731,6 +9697,9 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* return null(); if (propType == PropertyType::Normal) { + TokenPos exprPos; + if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) + return null(); Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!propExpr) @@ -9770,6 +9739,9 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* if (!handler.addPropertyDefinition(literal, propName, propExpr)) return null(); } + + if (possibleError) + checkDestructuringAssignmentElement(propExpr, exprPos, possibleError); } else if (propType == PropertyType::Shorthand) { /* * Support, e.g., |({x, y} = o)| as destructuring shorthand @@ -9784,6 +9756,9 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* if (!nameExpr) return null(); + if (possibleError) + checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError); + if (!handler.addShorthand(literal, propName, nameExpr)) return null(); } else if (propType == PropertyType::CoverInitializedName) { @@ -9820,6 +9795,12 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + return null(); + } + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!rhs) return null(); @@ -9832,9 +9813,6 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* if (!handler.addPropertyDefinition(literal, propName, propExpr)) return null(); - - if (!abortIfSyntaxParser()) - return null(); } else { RootedAtom funName(context); if (!tokenStream.isCurrentTokenType(TOK_RB)) { @@ -9856,6 +9834,9 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* JSOp op = JSOpFromPropertyType(propType); if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) return null(); + + if (possibleError) + possibleError->setPendingDestructuringErrorAt(namePos, JSMSG_BAD_DESTRUCT_TARGET); } if (!tokenStream.getToken(&tt)) diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 8bd691fc4..88d2dad18 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -877,6 +877,12 @@ class ParserBase : public StrictModeGetter */ MOZ_MUST_USE bool extraWarning(unsigned errorNumber, ...); + /* + * If extra warnings are enabled, report the given warning at the given + * offset. + */ + MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...); + bool isValidStrictBinding(PropertyName* name); bool warnOnceAboutExprClosure(); @@ -941,7 +947,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter class MOZ_STACK_CLASS PossibleError { private: - enum class ErrorKind { Expression, Destructuring }; + enum class ErrorKind { Expression, Destructuring, DestructuringWarning }; enum class ErrorState { None, Pending }; @@ -956,11 +962,12 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Parser& parser_; Error exprError_; Error destructuringError_; + Error destructuringWarning_; // Returns the error report. Error& error(ErrorKind kind); - // Return true if an error is pending without reporting + // Return true if an error is pending without reporting. bool hasError(ErrorKind kind); // Resolve any pending error. @@ -972,7 +979,11 @@ class Parser final : public ParserBase, private JS::AutoGCRooter // If there is a pending error, report it and return false, otherwise // return true. - bool checkForError(ErrorKind kind); + MOZ_MUST_USE bool checkForError(ErrorKind kind); + + // If there is a pending warning, report it and return either false or + // true depending on the werror option, otherwise return true. + MOZ_MUST_USE bool checkForWarning(ErrorKind kind); // Transfer an existing error to another instance. void transferErrorTo(ErrorKind kind, PossibleError* other); @@ -980,23 +991,33 @@ class Parser final : public ParserBase, private JS::AutoGCRooter public: explicit PossibleError(Parser& parser); + // Return true if a pending destructuring error is present. + bool hasPendingDestructuringError(); + // Set a pending destructuring error. Only a single error may be set // per instance, i.e. subsequent calls to this method are ignored and // won't overwrite the existing pending error. void setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber); + // Set a pending destructuring warning. Only a single warning may be + // set per instance, i.e. subsequent calls to this method are ignored + // and won't overwrite the existing pending warning. + void setPendingDestructuringWarningAt(const TokenPos& pos, unsigned errorNumber); + // Set a pending expression error. Only a single error may be set per // instance, i.e. subsequent calls to this method are ignored and won't // overwrite the existing pending error. void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber); - // If there is a pending destructuring error, report it and return - // false, otherwise return true. Clears any pending expression error. - bool checkForDestructuringError(); + // If there is a pending destructuring error or warning, report it and + // return false, otherwise return true. Clears any pending expression + // error. + MOZ_MUST_USE bool checkForDestructuringErrorOrWarning(); // If there is a pending expression error, report it and return false, - // otherwise return true. Clears any pending destructuring error. - bool checkForExpressionError(); + // otherwise return true. Clears any pending destructuring error or + // warning. + MOZ_MUST_USE bool checkForExpressionError(); // Pass pending errors between possible error instances. This is useful // for extending the lifetime of a pending error beyond the scope of @@ -1496,18 +1517,10 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling); Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling); - // Top-level entrypoint into destructuring assignment pattern checking and - // name-analyzing. - bool checkDestructuringAssignmentPattern(Node pattern, - PossibleError* possibleError = nullptr); - - // Recursive methods for checking/name-analyzing subcomponents of an - // destructuring assignment pattern. The array/object methods *must* be - // passed arrays or objects. The name method may be passed anything but - // will report an error if not passed a name. - bool checkDestructuringAssignmentArray(Node arrayPattern); - bool checkDestructuringAssignmentObject(Node objectPattern); - bool checkDestructuringAssignmentName(Node expr); + void checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos, + PossibleError* possibleError); + void checkDestructuringAssignmentElement(Node expr, TokenPos exprPos, + PossibleError* possibleError); Node newNumber(const Token& tok) { return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 895ba6416..7de6a242e 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -561,6 +561,10 @@ class SyntaxParseHandler node == NodeParenthesizedName; } + bool isArgumentsAnyParentheses(Node node, ExclusiveContext* cx) { + return node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName; + } + bool isEvalAnyParentheses(Node node, ExclusiveContext* cx) { return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName; } @@ -571,7 +575,7 @@ class SyntaxParseHandler if (isEvalAnyParentheses(node, cx)) return js_eval_str; - if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName) + if (isArgumentsAnyParentheses(node, cx)) return js_arguments_str; return nullptr; } -- cgit v1.2.3 From aa65e8a189d81cba75f101d07ef591b751881fe8 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 15:34:51 -0400 Subject: Refactor abortIfSyntaxParser after destructring --- js/src/frontend/Parser.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c1e165db5..bbdffa064 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -757,7 +757,15 @@ ParserBase::reportNoOffset(ParseReportKind kind, bool strict, unsigned errorNumb } template <> -bool +inline bool +Parser::abortIfSyntaxParser() +{ + handler.disableSyntaxParser(); + return true; +} + +template <> +inline bool Parser::abortIfSyntaxParser() { abortedSyntaxParse = true; @@ -4524,14 +4532,6 @@ Parser::arrayBindingPattern(DeclarationKind kind, YieldHandling yi return literal; } -template <> -bool -Parser::checkDestructuringAssignmentPattern(Node pattern, - PossibleError* possibleError /* = nullptr */) -{ - return abortIfSyntaxParser(); -} - template typename ParseHandler::Node Parser::destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling, -- cgit v1.2.3 From 449ea84dcc7dffb2f042fc414eb7238ae842d596 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 19:04:34 -0400 Subject: 1344477 - Part 1: Add JSOP_CALL_IGNORES_RV for function call that ignores return value. --- js/src/frontend/BytecodeEmitter.cpp | 64 ++++++++++++++++++++++--------------- js/src/frontend/BytecodeEmitter.h | 19 ++++++++--- 2 files changed, 53 insertions(+), 30 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 7582e8df1..c2eae4344 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4603,7 +4603,7 @@ BytecodeEmitter::emitSwitch(ParseNode* pn) // If the expression is a literal, suppress line number emission so // that debugging works more naturally. if (caseValue) { - if (!emitTree(caseValue, + if (!emitTree(caseValue, ValueUsage::WantValue, caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE)) { return false; @@ -6652,7 +6652,7 @@ BytecodeEmitter::emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLi } // Line notes were updated by emitLexicalScope. - return emitTree(body, emitLineNote); + return emitTree(body, ValueUsage::WantValue, emitLineNote); } // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See @@ -6754,9 +6754,9 @@ BytecodeEmitter::emitRequireObjectCoercible() return false; if (!emit2(JSOP_PICK, 2)) // VAL REQUIREOBJECTCOERCIBLE UNDEFINED VAL return false; - if (!emitCall(JSOP_CALL, 1)) // VAL IGNORED + if (!emitCall(JSOP_CALL_IGNORES_RV, 1))// VAL IGNORED return false; - checkTypeSet(JSOP_CALL); + checkTypeSet(JSOP_CALL_IGNORES_RV); if (!emit1(JSOP_POP)) // VAL return false; @@ -7276,12 +7276,14 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc // scope, but we still need to emit code for the initializers.) if (!updateSourceCoordNotes(init->pn_pos.begin)) return false; - if (!emitTree(init)) - return false; - - if (!init->isForLoopDeclaration()) { + if (init->isForLoopDeclaration()) { + if (!emitTree(init)) + return false; + } else { // 'init' is an expression, not a declaration. emitTree left its // value on the stack. + if (!emitTree(init, ValueUsage::IgnoreValue)) + return false; if (!emit1(JSOP_POP)) return false; } @@ -7363,7 +7365,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc if (!updateSourceCoordNotes(update->pn_pos.begin)) return false; - if (!emitTree(update)) + if (!emitTree(update, ValueUsage::IgnoreValue)) return false; if (!emit1(JSOP_POP)) return false; @@ -8690,8 +8692,9 @@ BytecodeEmitter::emitStatement(ParseNode* pn) if (useful) { JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP; + ValueUsage valueUsage = wantval ? ValueUsage::WantValue : ValueUsage::IgnoreValue; MOZ_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP)); - if (!emitTree(pn2)) + if (!emitTree(pn2, valueUsage)) return false; if (!emit1(op)) return false; @@ -9041,7 +9044,7 @@ BytecodeEmitter::emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitte } bool -BytecodeEmitter::emitCallOrNew(ParseNode* pn) +BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */) { bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE); /* @@ -9220,13 +9223,20 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) } if (!spread) { - if (!emitCall(pn->getOp(), argc, pn)) - return false; + if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) { + if (!emitCall(JSOP_CALL_IGNORES_RV, argc, pn)) + return false; + checkTypeSet(JSOP_CALL_IGNORES_RV); + } else { + if (!emitCall(pn->getOp(), argc, pn)) + return false; + checkTypeSet(pn->getOp()); + } } else { if (!emit1(pn->getOp())) return false; + checkTypeSet(pn->getOp()); } - checkTypeSet(pn->getOp()); if (pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) || pn->isOp(JSOP_SPREADEVAL) || @@ -9324,12 +9334,13 @@ BytecodeEmitter::emitLogical(ParseNode* pn) } bool -BytecodeEmitter::emitSequenceExpr(ParseNode* pn) +BytecodeEmitter::emitSequenceExpr(ParseNode* pn, + ValueUsage valueUsage /* = ValueUsage::WantValue */) { for (ParseNode* child = pn->pn_head; ; child = child->pn_next) { if (!updateSourceCoordNotes(child->pn_pos.begin)) return false; - if (!emitTree(child)) + if (!emitTree(child, child->pn_next ? ValueUsage::IgnoreValue : valueUsage)) return false; if (!child->pn_next) break; @@ -9392,7 +9403,8 @@ BytecodeEmitter::emitLabeledStatement(const LabeledStatement* pn) } bool -BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional) +BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional, + ValueUsage valueUsage /* = ValueUsage::WantValue */) { /* Emit the condition, then branch if false to the else part. */ if (!emitTree(&conditional.condition())) @@ -9402,13 +9414,13 @@ BytecodeEmitter::emitConditionalExpression(ConditionalExpression& conditional) if (!ifThenElse.emitCond()) return false; - if (!emitTreeInBranch(&conditional.thenExpression())) + if (!emitTreeInBranch(&conditional.thenExpression(), valueUsage)) return false; if (!ifThenElse.emitElse()) return false; - if (!emitTreeInBranch(&conditional.elseExpression())) + if (!emitTreeInBranch(&conditional.elseExpression(), valueUsage)) return false; if (!ifThenElse.emitEnd()) @@ -10288,7 +10300,8 @@ BytecodeEmitter::emitClass(ParseNode* pn) } bool -BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) +BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */, + EmitLineNumberNote emitLineNote /* = EMIT_LINENOTE */) { JS_CHECK_RECURSION(cx, return false); @@ -10410,7 +10423,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) break; case PNK_COMMA: - if (!emitSequenceExpr(pn)) + if (!emitSequenceExpr(pn, valueUsage)) return false; break; @@ -10432,7 +10445,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) break; case PNK_CONDITIONAL: - if (!emitConditionalExpression(pn->as())) + if (!emitConditionalExpression(pn->as(), valueUsage)) return false; break; @@ -10545,7 +10558,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) case PNK_CALL: case PNK_GENEXP: case PNK_SUPERCALL: - if (!emitCallOrNew(pn)) + if (!emitCallOrNew(pn, valueUsage)) return false; break; @@ -10703,12 +10716,13 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote) } bool -BytecodeEmitter::emitTreeInBranch(ParseNode* pn) +BytecodeEmitter::emitTreeInBranch(ParseNode* pn, + ValueUsage valueUsage /* = ValueUsage::WantValue */) { // Code that may be conditionally executed always need their own TDZ // cache. TDZCheckCache tdzCache(this); - return emitTree(pn); + return emitTree(pn, valueUsage); } static bool diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 4e5209593..99d6a2ff7 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -171,6 +171,11 @@ struct JumpList { void patchAll(jsbytecode* code, JumpTarget target); }; +enum class ValueUsage { + WantValue, + IgnoreValue +}; + struct MOZ_STACK_CLASS BytecodeEmitter { class TDZCheckCache; @@ -434,10 +439,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter }; // Emit code for the tree rooted at pn. - MOZ_MUST_USE bool emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); + MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue, + EmitLineNumberNote emitLineNote = EMIT_LINENOTE); // Emit code for the tree rooted at pn with its own TDZ cache. - MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn); + MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn, + ValueUsage valueUsage = ValueUsage::WantValue); // Emit global, eval, or module code for tree rooted at body. Always // encompasses the entire source. @@ -724,16 +731,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitRightAssociative(ParseNode* pn); MOZ_MUST_USE bool emitLeftAssociative(ParseNode* pn); MOZ_MUST_USE bool emitLogical(ParseNode* pn); - MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn); + MOZ_MUST_USE bool emitSequenceExpr(ParseNode* pn, + ValueUsage valueUsage = ValueUsage::WantValue); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitIncOrDec(ParseNode* pn); - MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional); + MOZ_MUST_USE bool emitConditionalExpression(ConditionalExpression& conditional, + ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result); MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted); - MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn); + MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue); MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn); MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn); -- cgit v1.2.3 From 6444d5d0ef586bda0a6e2745847fc1228ad7e5a2 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 16:18:57 -0400 Subject: 1339395 - Part 1: Align parse method for object literals to match array literals. --- js/src/frontend/FullParseHandler.h | 5 +- js/src/frontend/Parser.cpp | 377 +++++++++++++++++++------------------ 2 files changed, 199 insertions(+), 183 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index c7a7e0843..d34cdf43d 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -302,10 +302,9 @@ class FullParseHandler } MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) { - TokenPos pos(begin, inner->pn_pos.end); - ParseNode* spread = new_(PNK_SPREAD, JSOP_NOP, pos, inner); + ParseNode* spread = newSpread(begin, inner); if (!spread) - return null(); + return false; literal->append(spread); literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; return true; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index bbdffa064..585eef59d 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4361,85 +4361,90 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y RootedAtom propAtom(context); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt)) + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) break; - TokenPos namePos = pos(); - - tokenStream.ungetToken(); - - PropertyType propType; - Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); - if (!propName) + if (tt == TOK_TRIPLEDOT) { + // TODO: rest-binding property + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); + } else { + TokenPos namePos = tokenStream.nextToken().pos; - if (propType == PropertyType::Normal) { - // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. - - if (!tokenStream.getToken(&tt, TokenStream::Operand)) + PropertyType propType; + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); + if (!propName) return null(); + if (propType == PropertyType::Normal) { + // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. - Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); - if (!binding) - return null(); + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); - bool hasInitializer; - if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) - return null(); + Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!binding) + return null(); - Node bindingExpr = hasInitializer - ? bindingInitializer(binding, kind, yieldHandling) - : binding; - if (!bindingExpr) - return null(); + bool hasInitializer; + if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) + return null(); - if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) - return null(); - } else if (propType == PropertyType::Shorthand) { - // Handle e.g., |var {x, y} = o| as destructuring shorthand - // for |var {x: x, y: y} = o|. - MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + Node bindingExpr = hasInitializer + ? bindingInitializer(binding, kind, yieldHandling) + : binding; + if (!bindingExpr) + return null(); - Node binding = bindingIdentifier(kind, yieldHandling); - if (!binding) - return null(); + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else if (propType == PropertyType::Shorthand) { + // Handle e.g., |var {x, y} = o| as destructuring shorthand + // for |var {x: x, y: y} = o|. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); - if (!handler.addShorthand(literal, propName, binding)) - return null(); - } else if (propType == PropertyType::CoverInitializedName) { - // Handle e.g., |var {x=1, y=2} = o| as destructuring shorthand - // with default values. - MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); - Node binding = bindingIdentifier(kind, yieldHandling); - if (!binding) - return null(); + if (!handler.addShorthand(literal, propName, binding)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + // Handle e.g., |var {x=1, y=2} = o| as destructuring + // shorthand with default values. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); - tokenStream.consumeKnownToken(TOK_ASSIGN); + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); - Node bindingExpr = bindingInitializer(binding, kind, yieldHandling); - if (!bindingExpr) - return null(); + tokenStream.consumeKnownToken(TOK_ASSIGN); + + Node bindingExpr = bindingInitializer(binding, kind, yieldHandling); + if (!bindingExpr) + return null(); - if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else { + errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); return null(); - } else { - errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); - return null(); + + } } - if (!tokenStream.getToken(&tt)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); - if (tt == TOK_RC) + if (!matched) break; - if (tt != TOK_COMMA) { - reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin); - return null(); - } } + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, + JSMSG_CURLY_OPENED, begin)); + handler.setEndPosition(literal, pos().end); return literal; } @@ -9682,173 +9687,185 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* RootedAtom propAtom(context); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt)) + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) break; - TokenPos namePos = pos(); - - tokenStream.ungetToken(); - - PropertyType propType; - Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); - if (!propName) + if (tt == TOK_TRIPLEDOT) { + // TODO: object spread + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); + } else { + TokenPos namePos = tokenStream.nextToken().pos; + + PropertyType propType; + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); + if (!propName) - if (propType == PropertyType::Normal) { - TokenPos exprPos; - if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) - return null(); - Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, - possibleError); - if (!propExpr) return null(); - handler.checkAndSetIsDirectRHSAnonFunction(propExpr); + if (propType == PropertyType::Normal) { + TokenPos exprPos; + if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) + return null(); - if (foldConstants && !FoldConstants(context, &propExpr, this)) - return null(); + Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); + if (!propExpr) + return null(); - if (propAtom == context->names().proto) { - if (seenPrototypeMutation) { - // Directly report the error when we're not in a - // destructuring context. - if (!possibleError) { - errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); - return null(); - } + handler.checkAndSetIsDirectRHSAnonFunction(propExpr); - // Otherwise delay error reporting until we've determined - // whether or not we're destructuring. - possibleError->setPendingExpressionErrorAt(namePos, - JSMSG_DUPLICATE_PROTO_PROPERTY); - } - seenPrototypeMutation = true; + if (possibleError) + checkDestructuringAssignmentElement(propExpr, exprPos, possibleError); - // Note: this occurs *only* if we observe TOK_COLON! Only - // __proto__: v mutates [[Prototype]]. Getters, setters, - // method/generator definitions, computed property name - // versions of all of these, and shorthands do not. - if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) + if (foldConstants && !FoldConstants(context, &propExpr, this)) return null(); - } else { - if (!handler.isConstant(propExpr)) - handler.setListFlag(literal, PNX_NONCONST); - if (!handler.addPropertyDefinition(literal, propName, propExpr)) + if (propAtom == context->names().proto) { + if (seenPrototypeMutation) { + // Directly report the error when we're not in a + // destructuring context. + if (!possibleError) { + errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); + return null(); + } + + // Otherwise delay error reporting until we've + // determined whether or not we're destructuring. + possibleError->setPendingExpressionErrorAt(namePos, + JSMSG_DUPLICATE_PROTO_PROPERTY); + } + seenPrototypeMutation = true; + + // Note: this occurs *only* if we observe TOK_COLON! Only + // __proto__: v mutates [[Prototype]]. Getters, setters, + // method/generator definitions, computed property name + // versions of all of these, and shorthands do not. + if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) + return null(); + } else { + if (!handler.isConstant(propExpr)) + handler.setListFlag(literal, PNX_NONCONST); + + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } + } else if (propType == PropertyType::Shorthand) { + /* + * Support, e.g., |({x, y} = o)| as destructuring shorthand + * for |({x: x, y: y} = o)|, and |var o = {x, y}| as + * initializer shorthand for |var o = {x: x, y: y}|. + */ + Rooted name(context, identifierReference(yieldHandling)); + if (!name) return null(); - } - if (possibleError) - checkDestructuringAssignmentElement(propExpr, exprPos, possibleError); - } else if (propType == PropertyType::Shorthand) { - /* - * Support, e.g., |({x, y} = o)| as destructuring shorthand - * for |({x: x, y: y} = o)|, and |var o = {x, y}| as initializer - * shorthand for |var o = {x: x, y: y}|. - */ - Rooted name(context, identifierReference(yieldHandling)); - if (!name) - return null(); + Node nameExpr = identifierReference(name); + if (!nameExpr) + return null(); - Node nameExpr = identifierReference(name); - if (!nameExpr) - return null(); + if (possibleError) + checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError); - if (possibleError) - checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError); + if (!handler.addShorthand(literal, propName, nameExpr)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + /* + * Support, e.g., |({x=1, y=2} = o)| as destructuring + * shorthand with default values, as per ES6 12.14.5 + */ + Rooted name(context, identifierReference(yieldHandling)); + if (!name) + return null(); - if (!handler.addShorthand(literal, propName, nameExpr)) - return null(); - } else if (propType == PropertyType::CoverInitializedName) { - /* - * Support, e.g., |({x=1, y=2} = o)| as destructuring shorthand - * with default values, as per ES6 12.14.5 - */ - Rooted name(context, identifierReference(yieldHandling)); - if (!name) - return null(); + Node lhs = identifierReference(name); + if (!lhs) + return null(); - Node lhs = identifierReference(name); - if (!lhs) - return null(); + tokenStream.consumeKnownToken(TOK_ASSIGN); - tokenStream.consumeKnownToken(TOK_ASSIGN); + if (!seenCoverInitializedName) { + // "shorthand default" or "CoverInitializedName" syntax is + // only valid in the case of destructuring. + seenCoverInitializedName = true; - if (!seenCoverInitializedName) { - // "shorthand default" or "CoverInitializedName" syntax is only - // valid in the case of destructuring. - seenCoverInitializedName = true; + if (!possibleError) { + // Destructuring defaults are definitely not allowed + // in this object literal, because of something the + // caller knows about the preceding code. For example, + // maybe the preceding token is an operator: + // |x + {y=z}|. + error(JSMSG_COLON_AFTER_ID); + return null(); + } - if (!possibleError) { - // Destructuring defaults are definitely not allowed in this object literal, - // because of something the caller knows about the preceding code. - // For example, maybe the preceding token is an operator: `x + {y=z}`. - error(JSMSG_COLON_AFTER_ID); - return null(); + // Here we set a pending error so that later in the parse, + // once we've determined whether or not we're + // destructuring, the error can be reported or ignored + // appropriately. + possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } - // Here we set a pending error so that later in the parse, once we've - // determined whether or not we're destructuring, the error can be - // reported or ignored appropriately. - possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); - } + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + return null(); + } - if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { - // |chars| is "arguments" or "eval" here. - if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) return null(); - } - Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); - if (!rhs) - return null(); + handler.checkAndSetIsDirectRHSAnonFunction(rhs); - handler.checkAndSetIsDirectRHSAnonFunction(rhs); - - Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); - if (!propExpr) - return null(); + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + if (!propExpr) + return null(); - if (!handler.addPropertyDefinition(literal, propName, propExpr)) - return null(); - } else { - RootedAtom funName(context); - if (!tokenStream.isCurrentTokenType(TOK_RB)) { - funName = propAtom; + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } else { + RootedAtom funName(context); + if (!tokenStream.isCurrentTokenType(TOK_RB)) { + funName = propAtom; - if (propType == PropertyType::Getter || propType == PropertyType::Setter) { - funName = prefixAccessorName(propType, propAtom); - if (!funName) - return null(); + if (propType == PropertyType::Getter || propType == PropertyType::Setter) { + funName = prefixAccessorName(propType, propAtom); + if (!funName) + return null(); + } } - } - Node fn = methodDefinition(namePos.begin, propType, funName); - if (!fn) - return null(); + Node fn = methodDefinition(namePos.begin, propType, funName); + if (!fn) + return null(); - handler.checkAndSetIsDirectRHSAnonFunction(fn); + handler.checkAndSetIsDirectRHSAnonFunction(fn); - JSOp op = JSOpFromPropertyType(propType); - if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) - return null(); + JSOp op = JSOpFromPropertyType(propType); + if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) + return null(); - if (possibleError) - possibleError->setPendingDestructuringErrorAt(namePos, JSMSG_BAD_DESTRUCT_TARGET); + if (possibleError) { + possibleError->setPendingDestructuringErrorAt(namePos, + JSMSG_BAD_DESTRUCT_TARGET); + } + } } - if (!tokenStream.getToken(&tt)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); - if (tt == TOK_RC) + if (!matched) break; - if (tt != TOK_COMMA) { - reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos); - return null(); - } } + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, + JSMSG_CURLY_OPENED, openedPos)); + handler.setEndPosition(literal, pos().end); return literal; } -- cgit v1.2.3 From 638a904d0dbe4bcc5a625ea472c7e65ac75dbc06 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 16:28:47 -0400 Subject: Refactor Token& nextToken --- js/src/frontend/TokenStream.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index e92de4b03..2744fd144 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -571,6 +571,11 @@ class MOZ_STACK_CLASS TokenStream #endif } + const Token& nextToken() const { + MOZ_ASSERT(hasLookahead()); + return tokens[(cursor + 1) & ntokensMask]; + } + // Advance to the next token. If the token stream encountered an error, // return false. Otherwise return true and store the token kind in |*ttp|. MOZ_MUST_USE bool getToken(TokenKind* ttp, Modifier modifier = None) { @@ -1017,10 +1022,6 @@ class MOZ_STACK_CLASS TokenStream void updateLineInfoForEOL(); void updateFlagsForEOL(); - const Token& nextToken() const { - MOZ_ASSERT(hasLookahead()); - return tokens[(cursor + 1) & ntokensMask]; - } bool hasLookahead() const { return lookahead > 0; } -- cgit v1.2.3 From 091d00f1bd22f821d3926c46dd1d6cf7fbc746f4 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 16:37:57 -0400 Subject: 1339395 - Part 2: Add parser support for rest and spread object properties. --- js/src/frontend/BytecodeEmitter.cpp | 12 +++++++++++ js/src/frontend/FullParseHandler.h | 12 +++++++++++ js/src/frontend/Parser.cpp | 42 ++++++++++++++++++++++++++++++------ js/src/frontend/SyntaxParseHandler.h | 1 + 4 files changed, 61 insertions(+), 6 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c2eae4344..f9a1f8675 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5815,6 +5815,11 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla return false; for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { + if (member->isKind(PNK_SPREAD)) { + // FIXME: Implement + continue; + } + ParseNode* subpattern; if (member->isKind(PNK_MUTATEPROTO)) subpattern = member->pn_kid; @@ -9449,6 +9454,13 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, continue; } + if (propdef->isKind(PNK_SPREAD)) { + MOZ_ASSERT(type == ObjectLiteral); + objp.set(nullptr); + // FIXME: implement + continue; + } + bool extraPop = false; if (type == ClassBody && propdef->as().isStatic()) { extraPop = true; diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index d34cdf43d..2d7f57e1e 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -395,6 +395,18 @@ class FullParseHandler return true; } + MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) { + MOZ_ASSERT(literal->isKind(PNK_OBJECT)); + MOZ_ASSERT(literal->isArity(PN_LIST)); + + setListFlag(literal, PNX_NONCONST); + ParseNode* spread = newSpread(begin, inner); + if (!spread) + return false; + literal->append(spread); + return true; + } + MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn, JSOp op) { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 585eef59d..47e0f943d 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4367,9 +4367,20 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y break; if (tt == TOK_TRIPLEDOT) { - // TODO: rest-binding property - error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); - return null(); + // rest-binding property + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); + uint32_t begin = pos().begin; + + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + + Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!inner) + return null(); + + if (!handler.addSpreadProperty(literal, begin, inner)) + return null(); } else { TokenPos namePos = tokenStream.nextToken().pos; @@ -4439,6 +4450,10 @@ Parser::objectBindingPattern(DeclarationKind kind, YieldHandling y return null(); if (!matched) break; + if (tt == TOK_TRIPLEDOT) { + error(JSMSG_REST_WITH_COMMA); + return null(); + } } MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, @@ -9693,9 +9708,22 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* break; if (tt == TOK_TRIPLEDOT) { - // TODO: object spread - error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); - return null(); + // object spread + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); + uint32_t begin = pos().begin; + + TokenPos innerPos; + if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand)) + return null(); + + Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); + if (!inner) + return null(); + if (possibleError) + checkDestructuringAssignmentTarget(inner, innerPos, possibleError); + if (!handler.addSpreadProperty(literal, begin, inner)) + return null(); } else { TokenPos namePos = tokenStream.nextToken().pos; @@ -9860,6 +9888,8 @@ Parser::objectLiteral(YieldHandling yieldHandling, PossibleError* return null(); if (!matched) break; + if (tt == TOK_TRIPLEDOT && possibleError) + possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 7de6a242e..a604b599f 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -298,6 +298,7 @@ class SyntaxParseHandler MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; } MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; } + MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; } MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } -- cgit v1.2.3 From 3048ff4346bf2021059bdecec6343c73bb26833a Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 20:14:28 -0400 Subject: 1339395 - Part 3: Add BytecodeEmitter support for object rest and spread properties. --- js/src/frontend/BytecodeEmitter.cpp | 304 ++++++++++++++++++++++++++++++------ js/src/frontend/BytecodeEmitter.h | 15 ++ 2 files changed, 274 insertions(+), 45 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f9a1f8675..b3b658a2d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5814,41 +5814,78 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla if (!emitRequireObjectCoercible()) // ... RHS return false; - for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { - if (member->isKind(PNK_SPREAD)) { - // FIXME: Implement - continue; - } + bool needsRestPropertyExcludedSet = pattern->pn_count > 1 && + pattern->last()->isKind(PNK_SPREAD); + if (needsRestPropertyExcludedSet) { + if (!emitDestructuringObjRestExclusionSet(pattern)) // ... RHS SET + return false; + if (!emit1(JSOP_SWAP)) // ... SET RHS + return false; + } + + for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { ParseNode* subpattern; - if (member->isKind(PNK_MUTATEPROTO)) + if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD)) subpattern = member->pn_kid; else subpattern = member->pn_right; + ParseNode* lhs = subpattern; + MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN)); if (lhs->isKind(PNK_ASSIGN)) lhs = lhs->pn_left; size_t emitted; - if (!emitDestructuringLHSRef(lhs, &emitted)) // ... RHS *LREF + if (!emitDestructuringLHSRef(lhs, &emitted)) // ... *SET RHS *LREF return false; // Duplicate the value being destructured to use as a reference base. if (emitted) { - if (!emitDupAt(emitted)) // ... RHS *LREF RHS + if (!emitDupAt(emitted)) // ... *SET RHS *LREF RHS return false; } else { - if (!emit1(JSOP_DUP)) // ... RHS RHS + if (!emit1(JSOP_DUP)) // ... *SET RHS RHS return false; } + if (member->isKind(PNK_SPREAD)) { + if (!updateSourceCoordNotes(member->pn_pos.begin)) + return false; + + if (!emitNewInit(JSProto_Object)) // ... *SET RHS *LREF RHS TARGET + return false; + if (!emit1(JSOP_DUP)) // ... *SET RHS *LREF RHS TARGET TARGET + return false; + if (!emit2(JSOP_PICK, 2)) // ... *SET RHS *LREF TARGET TARGET RHS + return false; + + if (needsRestPropertyExcludedSet) { + if (!emit2(JSOP_PICK, emitted + 4)) // ... RHS *LREF TARGET TARGET RHS SET + return false; + } + + CopyOption option = needsRestPropertyExcludedSet + ? CopyOption::Filtered + : CopyOption::Unfiltered; + if (!emitCopyDataProperties(option)) // ... RHS *LREF TARGET + return false; + + // Destructure TARGET per this member's lhs. + if (!emitSetOrInitializeDestructuring(lhs, flav)) // ... RHS + return false; + + MOZ_ASSERT(member == pattern->last(), "Rest property is always last"); + break; + } + // Now push the property name currently being matched, which is the // current property name "label" on the left of a colon in the object // initialiser. bool needsGetElem = true; if (member->isKind(PNK_MUTATEPROTO)) { - if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS *LREF PROP + if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... *SET RHS *LREF PROP return false; needsGetElem = false; } else { @@ -5856,7 +5893,7 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla ParseNode* key = member->pn_left; if (key->isKind(PNK_NUMBER)) { - if (!emitNumberOp(key->pn_dval)) // ... RHS *LREF RHS KEY + if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY return false; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { PropertyName* name = key->pn_atom->asPropertyName(); @@ -5866,30 +5903,142 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla // as indexes for simplification of downstream analysis. jsid id = NameToId(name); if (id != IdToTypeId(id)) { - if (!emitTree(key)) // ... RHS *LREF RHS KEY + if (!emitTree(key)) // ... *SET RHS *LREF RHS KEY return false; } else { - if (!emitAtomOp(name, JSOP_GETPROP)) // ... RHS *LREF PROP + if (!emitAtomOp(name, JSOP_GETPROP)) // ... *SET RHS *LREF PROP return false; needsGetElem = false; } } else { - if (!emitComputedPropertyName(key)) // ... RHS *LREF RHS KEY + if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY return false; + + // Add the computed property key to the exclusion set. + if (needsRestPropertyExcludedSet) { + if (!emitDupAt(emitted + 3)) // ... SET RHS *LREF RHS KEY SET + return false; + if (!emitDupAt(1)) // ... SET RHS *LREF RHS KEY SET KEY + return false; + if (!emit1(JSOP_UNDEFINED)) // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED + return false; + if (!emit1(JSOP_INITELEM)) // ... SET RHS *LREF RHS KEY SET + return false; + if (!emit1(JSOP_POP)) // ... SET RHS *LREF RHS KEY + return false; + } } } // Get the property value if not done already. - if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS *LREF PROP + if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... *SET RHS *LREF PROP return false; if (subpattern->isKind(PNK_ASSIGN)) { - if (!emitDefault(subpattern->pn_right, lhs)) // ... RHS *LREF VALUE + if (!emitDefault(subpattern->pn_right, lhs)) // ... *SET RHS *LREF VALUE return false; } // Destructure PROP per this member's lhs. - if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... RHS + if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... *SET RHS + return false; + } + + return true; +} + +bool +BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern) +{ + MOZ_ASSERT(pattern->isKind(PNK_OBJECT)); + MOZ_ASSERT(pattern->isArity(PN_LIST)); + MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD)); + + ptrdiff_t offset = this->offset(); + if (!emitNewInit(JSProto_Object)) + return false; + + // Try to construct the shape of the object as we go, so we can emit a + // JSOP_NEWOBJECT with the final shape instead. + // In the case of computed property names and indices, we cannot fix the + // shape at bytecode compile time. When the shape cannot be determined, + // |obj| is nulled out. + + // No need to do any guessing for the object kind, since we know the upper + // bound of how many properties we plan to have. + gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1); + RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, kind, TenuredObject)); + if (!obj) + return false; + + RootedAtom pnatom(cx); + for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { + if (member->isKind(PNK_SPREAD)) + break; + + bool isIndex = false; + if (member->isKind(PNK_MUTATEPROTO)) { + pnatom.set(cx->names().proto); + } else { + ParseNode* key = member->pn_left; + if (key->isKind(PNK_NUMBER)) { + if (!emitNumberOp(key->pn_dval)) + return false; + isIndex = true; + } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { + // The parser already checked for atoms representing indexes and + // used PNK_NUMBER instead, but also watch for ids which TI treats + // as indexes for simplification of downstream analysis. + jsid id = NameToId(key->pn_atom->asPropertyName()); + if (id != IdToTypeId(id)) { + if (!emitTree(key)) + return false; + isIndex = true; + } else { + pnatom.set(key->pn_atom); + } + } else { + // Otherwise this is a computed property name which needs to + // be added dynamically. + obj.set(nullptr); + continue; + } + } + + // Initialize elements with |undefined|. + if (!emit1(JSOP_UNDEFINED)) + return false; + + if (isIndex) { + obj.set(nullptr); + if (!emit1(JSOP_INITELEM)) + return false; + } else { + uint32_t index; + if (!makeAtomIndex(pnatom, &index)) + return false; + + if (obj) { + MOZ_ASSERT(!obj->inDictionaryMode()); + Rooted id(cx, AtomToId(pnatom)); + if (!NativeDefineProperty(cx, obj, id, UndefinedHandleValue, nullptr, nullptr, + JSPROP_ENUMERATE)) + { + return false; + } + if (obj->inDictionaryMode()) + obj.set(nullptr); + } + + if (!emitIndex32(JSOP_INITPROP, index)) + return false; + } + } + + if (obj) { + // The object survived and has a predictable shape: update the + // original bytecode. + if (!replaceNewInitWithNewObject(obj, offset)) return false; } @@ -6770,6 +6919,53 @@ BytecodeEmitter::emitRequireObjectCoercible() return true; } +bool +BytecodeEmitter::emitCopyDataProperties(CopyOption option) +{ + DebugOnly depth = this->stackDepth; + + uint32_t argc; + if (option == CopyOption::Filtered) { + MOZ_ASSERT(depth > 2); // TARGET SOURCE SET + argc = 3; + + if (!emitAtomOp(cx->names().CopyDataProperties, + JSOP_GETINTRINSIC)) // TARGET SOURCE SET COPYDATAPROPERTIES + { + return false; + } + } else { + MOZ_ASSERT(depth > 1); // TARGET SOURCE + argc = 2; + + if (!emitAtomOp(cx->names().CopyDataPropertiesUnfiltered, + JSOP_GETINTRINSIC)) // TARGET SOURCE COPYDATAPROPERTIES + { + return false; + } + } + + if (!emit1(JSOP_UNDEFINED)) // TARGET SOURCE *SET COPYDATAPROPERTIES UNDEFINED + return false; + if (!emit2(JSOP_PICK, argc + 1)) // SOURCE *SET COPYDATAPROPERTIES UNDEFINED TARGET + return false; + if (!emit2(JSOP_PICK, argc + 1)) // *SET COPYDATAPROPERTIES UNDEFINED TARGET SOURCE + return false; + if (option == CopyOption::Filtered) { + if (!emit2(JSOP_PICK, argc + 1)) // COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET + return false; + } + if (!emitCall(JSOP_CALL_IGNORES_RV, argc)) // IGNORED + return false; + checkTypeSet(JSOP_CALL_IGNORES_RV); + + if (!emit1(JSOP_POP)) // - + return false; + + MOZ_ASSERT(depth - int(argc) == this->stackDepth); + return true; +} + bool BytecodeEmitter::emitIterator() { @@ -9456,8 +9652,17 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, if (propdef->isKind(PNK_SPREAD)) { MOZ_ASSERT(type == ObjectLiteral); + + if (!emit1(JSOP_DUP)) + return false; + + if (!emitTree(propdef->pn_kid)) + return false; + + if (!emitCopyDataProperties(CopyOption::Unfiltered)) + return false; + objp.set(nullptr); - // FIXME: implement continue; } @@ -9487,7 +9692,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, // The parser already checked for atoms representing indexes and // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simpliciation of downstream analysis. + // as indexes for simplification of downstream analysis. jsid id = NameToId(key->pn_atom->asPropertyName()); if (id != IdToTypeId(id)) { if (!emitTree(key)) @@ -9574,8 +9779,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, MOZ_ASSERT(!IsHiddenInitOp(op)); MOZ_ASSERT(!objp->inDictionaryMode()); Rooted id(cx, AtomToId(key->pn_atom)); - RootedValue undefinedValue(cx, UndefinedValue()); - if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr, + if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr, JSPROP_ENUMERATE)) { return false; @@ -9618,15 +9822,16 @@ BytecodeEmitter::emitObject(ParseNode* pn) if (!emitNewInit(JSProto_Object)) return false; - /* - * Try to construct the shape of the object as we go, so we can emit a - * JSOP_NEWOBJECT with the final shape instead. - */ - RootedPlainObject obj(cx); - // No need to do any guessing for the object kind, since we know exactly - // how many properties we plan to have. + // Try to construct the shape of the object as we go, so we can emit a + // JSOP_NEWOBJECT with the final shape instead. + // In the case of computed property names and indices, we cannot fix the + // shape at bytecode compile time. When the shape cannot be determined, + // |obj| is nulled out. + + // No need to do any guessing for the object kind, since we know the upper + // bound of how many properties we plan to have. gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count); - obj = NewBuiltinClassInstance(cx, kind, TenuredObject); + RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, kind, TenuredObject)); if (!obj) return false; @@ -9634,25 +9839,34 @@ BytecodeEmitter::emitObject(ParseNode* pn) return false; if (obj) { - /* - * The object survived and has a predictable shape: update the original - * bytecode. - */ - ObjectBox* objbox = parser->newObjectBox(obj); - if (!objbox) + // The object survived and has a predictable shape: update the original + // bytecode. + if (!replaceNewInitWithNewObject(obj, offset)) return false; + } - static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, - "newinit and newobject must have equal length to edit in-place"); + return true; +} - uint32_t index = objectList.add(objbox); - jsbytecode* code = this->code(offset); - code[0] = JSOP_NEWOBJECT; - code[1] = jsbytecode(index >> 24); - code[2] = jsbytecode(index >> 16); - code[3] = jsbytecode(index >> 8); - code[4] = jsbytecode(index); - } +bool +BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset) +{ + ObjectBox* objbox = parser->newObjectBox(obj); + if (!objbox) + return false; + + static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, + "newinit and newobject must have equal length to edit in-place"); + + uint32_t index = objectList.add(objbox); + jsbytecode* code = this->code(offset); + + MOZ_ASSERT(code[0] == JSOP_NEWINIT); + code[0] = JSOP_NEWOBJECT; + code[1] = jsbytecode(index >> 24); + code[2] = jsbytecode(index >> 16); + code[3] = jsbytecode(index >> 8); + code[4] = jsbytecode(index); return true; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 99d6a2ff7..595ee6405 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -540,6 +540,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn); + MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset); + MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn); MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, @@ -667,6 +669,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter // []/{} expression). MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav); + // emitDestructuringObjRestExclusionSet emits the property exclusion set + // for the rest-property in an object pattern. + MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern); + // emitDestructuringOps assumes the to-be-destructured value has been // pushed on the stack and emits code to destructure each part of a [] or // {} lhs expression. @@ -684,6 +690,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter // object, with no overall effect on the stack. MOZ_MUST_USE bool emitRequireObjectCoercible(); + enum class CopyOption { + Filtered, Unfiltered + }; + + // Calls either the |CopyDataProperties| or the + // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or + // two in the latter case) elements from the stack. + MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option); + // emitIterator expects the iterable to already be on the stack. // It will replace that stack value with the corresponding iterator MOZ_MUST_USE bool emitIterator(); -- cgit v1.2.3 From 221239a77c6d7de58424448de7b83af04c638378 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 20:59:26 -0400 Subject: 1339395 - Part 7: Remove no longer needed check for jsid strings which are indices from frontend. --- js/src/frontend/BytecodeEmitter.cpp | 39 ++++--------------------------------- js/src/frontend/FoldConstants.cpp | 7 ------- 2 files changed, 4 insertions(+), 42 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index b3b658a2d..309d6c290 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5896,20 +5896,9 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY return false; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { - PropertyName* name = key->pn_atom->asPropertyName(); - - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(name); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) // ... *SET RHS *LREF RHS KEY - return false; - } else { - if (!emitAtomOp(name, JSOP_GETPROP)) // ... *SET RHS *LREF PROP - return false; - needsGetElem = false; - } + if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) // ... *SET RHS *LREF PROP + return false; + needsGetElem = false; } else { if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY return false; @@ -5986,17 +5975,7 @@ BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern) return false; isIndex = true; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(key->pn_atom->asPropertyName()); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) - return false; - isIndex = true; - } else { - pnatom.set(key->pn_atom); - } + pnatom.set(key->pn_atom); } else { // Otherwise this is a computed property name which needs to // be added dynamically. @@ -9689,16 +9668,6 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, { continue; } - - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(key->pn_atom->asPropertyName()); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) - return false; - isIndex = true; - } } else { if (!emitComputedPropertyName(key)) return false; diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 441a72a45..689fa02b4 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -1345,15 +1345,8 @@ FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser& if (!name) return true; - // Also don't optimize if the name doesn't map directly to its id for TI's - // purposes. - if (NameToId(name) != IdToTypeId(NameToId(name))) - return true; - // Optimization 3: We have expr["foo"] where foo is not an index. Convert // to a property access (like expr.foo) that optimizes better downstream. - // Don't bother with this for names that TI considers to be indexes, to - // simplify downstream analysis. ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end); if (!dottedAccess) return false; -- cgit v1.2.3 From 4a84afdc892dcc3a1732b45e5ac65812daa53fae Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 14 Jul 2019 22:12:08 -0400 Subject: 1353691 - Report SyntaxError when arrow function has await-identifier in async function context. --- js/src/frontend/Parser.cpp | 59 ++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 25 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 47e0f943d..0c279591f 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3623,9 +3623,14 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, FunctionBox* funbox = pc->functionBox(); RootedFunction fun(context, funbox->function()); - AutoAwaitIsKeyword awaitIsKeyword(this, funbox->isAsync()); - if (!functionArguments(yieldHandling, kind, pn)) - return false; + // See below for an explanation why arrow function parameters and arrow + // function bodies are parsed with different yield/await settings. + { + bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword()); + AutoAwaitIsKeyword awaitIsKeyword(this, asyncOrArrowInAsync); + if (!functionArguments(yieldHandling, kind, pn)) + return false; + } Maybe varScope; if (funbox->hasParameterExprs) { @@ -3689,32 +3694,36 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, // |yield| in the parameters is either a name or keyword, depending on // whether the arrow function is enclosed in a generator function or not. // Whereas the |yield| in the function body is always parsed as a name. + // The same goes when parsing |await| in arrow functions. YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind()); - Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); - if (!body) - return false; + Node body; + { + AutoAwaitIsKeyword awaitIsKeyword(this, funbox->isAsync()); + body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); + if (!body) + return false; + } - if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) { + if ((kind == Statement || kind == Expression) && fun->explicitName()) { RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName()); - // `await` cannot be checked at this point because of different context. - // It should already be checked before this point. - if (propertyName != context->names().await) { - YieldHandling nameYieldHandling; - if (kind == Expression) { - // Named lambda has binding inside it. - nameYieldHandling = bodyYieldHandling; - } else { - // Otherwise YieldHandling cannot be checked at this point - // because of different context. - // It should already be checked before this point. - nameYieldHandling = YieldIsName; - } + YieldHandling nameYieldHandling; + if (kind == Expression) { + // Named lambda has binding inside it. + nameYieldHandling = bodyYieldHandling; + } else { + // Otherwise YieldHandling cannot be checked at this point + // because of different context. + // It should already be checked before this point. + nameYieldHandling = YieldIsName; + } - if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin, - nameYieldHandling)) - { - return false; - } + // We already use the correct await-handling at this point, therefore + // we don't need call AutoAwaitIsKeyword here. + + if (!checkBindingIdentifier(propertyName, handler.getPosition(pn).begin, + nameYieldHandling)) + { + return false; } } -- cgit v1.2.3