diff options
-rw-r--r-- | js/src/frontend/Parser.cpp | 398 | ||||
-rw-r--r-- | js/src/frontend/Parser.h | 30 |
2 files changed, 331 insertions, 97 deletions
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<ParseHandler>::PossibleError::transferErrorsTo(PossibleError* other) template <> bool -Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<DeclarationKind> maybeDecl) +Parser<FullParseHandler>::checkDestructuringAssignmentName(ParseNode* expr) { MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr)); @@ -4255,24 +4255,8 @@ Parser<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat return false; } - // This expression might be in a variable-binding pattern where only plain, - // unparenthesized names are permitted. - if (maybeDecl) { - // Destructuring patterns in declarations must only contain - // unparenthesized names. - if (!handler.isUnparenthesizedName(expr)) { - errorAt(expr->pn_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<FullParseHandler>::checkDestructuringName(ParseNode* expr, Maybe<Declarat template <> bool -Parser<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern, - Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError /* = nullptr */); +Parser<FullParseHandler>::checkDestructuringAssignmentPattern(ParseNode* pattern, + PossibleError* possibleError /* = nullptr */); template <> bool -Parser<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern, - Maybe<DeclarationKind> maybeDecl) +Parser<FullParseHandler>::checkDestructuringAssignmentObject(ParseNode* objectPattern) { MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT)); @@ -4319,10 +4301,10 @@ Parser<FullParseHandler>::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<FullParseHandler>::checkDestructuringObject(ParseNode* objectPattern, template <> bool -Parser<FullParseHandler>::checkDestructuringArray(ParseNode* arrayPattern, - Maybe<DeclarationKind> maybeDecl) +Parser<FullParseHandler>::checkDestructuringAssignmentArray(ParseNode* arrayPattern) { MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY)); @@ -4355,10 +4336,10 @@ Parser<FullParseHandler>::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<FullParseHandler>::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<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern, - Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError /* = nullptr */) +Parser<FullParseHandler>::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<FullParseHandler>::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<FullParseHandler>::checkDestructuringPattern(ParseNode* pattern, return isDestructuring; } +class AutoClearInDestructuringDecl +{ + ParseContext* pc_; + Maybe<DeclarationKind> 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> +typename ParseHandler::Node +Parser<ParseHandler>::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> +typename ParseHandler::Node +Parser<ParseHandler>::bindingIdentifier(DeclarationKind kind, YieldHandling yieldHandling) +{ + Rooted<PropertyName*> 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> +typename ParseHandler::Node +Parser<ParseHandler>::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> +typename ParseHandler::Node +Parser<ParseHandler>::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> +typename ParseHandler::Node +Parser<ParseHandler>::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<SyntaxParseHandler>::checkDestructuringPattern(Node pattern, - Maybe<DeclarationKind> maybeDecl, - PossibleError* possibleError /* = nullptr */) +Parser<SyntaxParseHandler>::checkDestructuringAssignmentPattern(Node pattern, + PossibleError* possibleError /* = nullptr */) { return abortIfSyntaxParser(); } @@ -4439,17 +4680,16 @@ Parser<ParseHandler>::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<ParseHandler>::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<ParseHandler>::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<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandli return handler.newConditional(condition, thenExpr, elseExpr); } -class AutoClearInDestructuringDecl -{ - ParseContext* pc_; - Maybe<DeclarationKind> 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> typename ParseHandler::Node Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling, @@ -8170,7 +8396,7 @@ Parser<ParseHandler>::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<ParseHandler>::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<ParseHandler>::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<PropertyName*> name(context, identifierReference(yieldHandling)); @@ -9609,7 +9835,7 @@ Parser<ParseHandler>::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<PropertyName*> 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<DeclarationKind> 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<DeclarationKind> maybeDecl); - bool checkDestructuringObject(Node objectPattern, mozilla::Maybe<DeclarationKind> maybeDecl); - bool checkDestructuringName(Node expr, mozilla::Maybe<DeclarationKind> 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); |