Bug 1303703 - Part 1: Separate binding pattern parsing from object/array literal parsing. r=shu

--HG--
extra : rebase_source : 75919aca29f8d2267efc2dd0012ec80f308081fc
This commit is contained in:
André Bargull 2017-04-22 02:04:27 -07:00
Родитель 63a52fdeab
Коммит 3eb0058955
2 изменённых файлов: 333 добавлений и 97 удалений

Просмотреть файл

@ -4286,8 +4286,7 @@ Parser<ParseHandler, CharT>::PossibleError::transferErrorsTo(PossibleError* othe
template <>
bool
Parser<FullParseHandler, char16_t>::checkDestructuringName(ParseNode* expr,
const Maybe<DeclarationKind>& maybeDecl)
Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentName(ParseNode* expr)
{
MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr));
@ -4299,24 +4298,8 @@ Parser<FullParseHandler, char16_t>::checkDestructuringName(ParseNode* expr,
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))
@ -4335,23 +4318,20 @@ Parser<FullParseHandler, char16_t>::checkDestructuringName(ParseNode* expr,
template <>
bool
Parser<FullParseHandler, char16_t>::checkDestructuringPattern(ParseNode* pattern,
const Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError /* = nullptr */);
Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentPattern(ParseNode* pattern,
PossibleError* possibleError /* = nullptr */);
template <>
bool
Parser<SyntaxParseHandler, char16_t>::checkDestructuringPattern(Node pattern,
const Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError /* = nullptr */)
Parser<SyntaxParseHandler, char16_t>::checkDestructuringAssignmentPattern(Node pattern,
PossibleError* possibleError /* = nullptr */)
{
return abortIfSyntaxParser();
}
template <>
bool
Parser<FullParseHandler, char16_t>::checkDestructuringObject(ParseNode* objectPattern,
const Maybe<DeclarationKind>& maybeDecl)
Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentObject(ParseNode* objectPattern)
{
MOZ_ASSERT(objectPattern->isKind(PNK_OBJECT));
@ -4372,10 +4352,10 @@ Parser<FullParseHandler, char16_t>::checkDestructuringObject(ParseNode* objectPa
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;
}
}
@ -4385,8 +4365,7 @@ Parser<FullParseHandler, char16_t>::checkDestructuringObject(ParseNode* objectPa
template <>
bool
Parser<FullParseHandler, char16_t>::checkDestructuringArray(ParseNode* arrayPattern,
const Maybe<DeclarationKind>& maybeDecl)
Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentArray(ParseNode* arrayPattern)
{
MOZ_ASSERT(arrayPattern->isKind(PNK_ARRAY));
@ -4408,10 +4387,10 @@ Parser<FullParseHandler, char16_t>::checkDestructuringArray(ParseNode* arrayPatt
}
if (handler.isUnparenthesizedDestructuringPattern(target)) {
if (!this->checkDestructuringPattern(target, maybeDecl))
if (!this->checkDestructuringAssignmentPattern(target))
return false;
} else {
if (!checkDestructuringName(target, maybeDecl))
if (!checkDestructuringAssignmentName(target))
return false;
}
}
@ -4432,32 +4411,28 @@ Parser<FullParseHandler, char16_t>::checkDestructuringArray(ParseNode* arrayPatt
* 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, char16_t>::checkDestructuringPattern(ParseNode* pattern,
const Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError /* = nullptr */)
Parser<FullParseHandler, char16_t>::checkDestructuringAssignmentPattern(ParseNode* pattern,
PossibleError* possibleError /* = nullptr */)
{
if (pattern->isKind(PNK_ARRAYCOMP)) {
errorAt(pattern->pn_pos.begin, JSMSG_ARRAY_COMP_LEFTSIDE);
@ -4465,8 +4440,8 @@ Parser<FullParseHandler, char16_t>::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())
@ -4475,6 +4450,274 @@ Parser<FullParseHandler, char16_t>::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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::objectBindingPattern(DeclarationKind kind,
YieldHandling yieldHandling)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
if (!CheckRecursionLimit(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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
if (!CheckRecursionLimit(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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
@ -4484,17 +4727,16 @@ Parser<ParseHandler, CharT>::destructuringDeclaration(DeclarationKind kind,
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;
}
@ -4611,6 +4853,12 @@ Parser<ParseHandler, CharT>::declarationPattern(Node decl, DeclarationKind declK
// 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);
}
@ -6041,7 +6289,7 @@ Parser<ParseHandler, CharT>::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);
@ -7988,26 +8236,6 @@ Parser<ParseHandler, CharT>::condExpr1(InHandling inHandling, YieldHandling yiel
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 <template <typename CharT> class ParseHandler, typename CharT>
typename ParseHandler<CharT>::Node
Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
@ -8239,7 +8467,7 @@ Parser<ParseHandler, CharT>::assignExpr(InHandling inHandling, YieldHandling yie
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)) {
@ -9589,7 +9817,7 @@ Parser<ParseHandler, CharT>::computedPropertyName(YieldHandling yieldHandling, N
// 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)
@ -9678,8 +9906,8 @@ Parser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
}
} 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));
@ -9694,7 +9922,7 @@ Parser<ParseHandler, CharT>::objectLiteral(YieldHandling yieldHandling,
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));

Просмотреть файл

@ -1526,17 +1526,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, const mozilla::Maybe<DeclarationKind>& maybeDecl,
PossibleError* possibleError = nullptr);
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);
// 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, const mozilla::Maybe<DeclarationKind>& maybeDecl);
bool checkDestructuringObject(Node objectPattern, const mozilla::Maybe<DeclarationKind>& maybeDecl);
bool checkDestructuringName(Node expr, const mozilla::Maybe<DeclarationKind>& maybeDecl);
// 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);