--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -721,17 +721,16 @@ Parser<ParseHandler>::checkOptions()
return true;
}
template <typename ParseHandler>
Parser<ParseHandler>::~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();
@@ -3934,16 +3933,80 @@ bool
Parser<ParseHandler>::AutoPushStmtInfoPC::makeInnermostLexicalScope(StaticBlockScope& blockScope)
{
MOZ_ASSERT(parser_.pc->stmtStack.innermost() == &stmt_);
parser_.pc->stmtStack.makeInnermostLexicalScope(blockScope);
return generateBlockId();
}
template <typename ParseHandler>
+Parser<ParseHandler>::PossibleError::PossibleError(Parser<ParseHandler>& parser)
+ : parser_(parser)
+{
+ state_ = ErrorState::None;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setPending(ParseReportKind kind, unsigned errorNumber,
+ bool strict)
+{
+ // If we report an error later, we'll do it from the position where we set
+ // the state to pending.
+ offset_ = parser_.pos().begin;
+ reportKind_ = kind;
+ strict_ = strict;
+ errorNumber_ = errorNumber;
+ state_ = ErrorState::Pending;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::setResolved()
+{
+ state_ = ErrorState::None;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::hasError()
+{
+ return state_ == ErrorState::Pending;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::PossibleError::checkForExprErrors()
+{
+ bool err = hasError();
+ if (err) {
+ parser_.reportWithOffset(reportKind_, strict_, offset_, errorNumber_);
+ }
+ return !err;
+}
+
+template <typename ParseHandler>
+void
+Parser<ParseHandler>::PossibleError::transferErrorTo(PossibleError* other)
+{
+ if (other) {
+ MOZ_ASSERT(this != other);
+ MOZ_ASSERT(!other->hasError());
+ // We should never allow fields to be copied between instances
+ // that point to different underlying parsers.
+ MOZ_ASSERT(&parser_ == &other->parser_);
+ other->offset_ = offset_;
+ other->reportKind_ = reportKind_;
+ other->errorNumber_ = errorNumber_;
+ other->strict_ = strict_;
+ other->state_ = state_;
+ }
+}
+
+template <typename ParseHandler>
static inline bool
HasOuterLexicalBinding(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom)
{
while (stmt->enclosingScope) {
stmt = LexicalLookup(pc, atom, stmt->enclosingScope);
if (!stmt)
return false;
if (stmt->type == StmtType::BLOCK)
@@ -4424,17 +4487,18 @@ Parser<SyntaxParseHandler>::checkDestruc
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
TokenKind tt)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
pc->inDeclDestructuring = true;
- Node pn = primaryExpr(yieldHandling, TripledotProhibited, tt);
+ Node pn = primaryExpr(yieldHandling, TripledotProhibited,
+ nullptr /* possibleError */, tt);
pc->inDeclDestructuring = false;
if (!pn)
return null();
if (!checkDestructuringPattern(data, pn))
return null();
return pn;
}
@@ -4577,36 +4641,40 @@ Parser<ParseHandler>::newBindingNode(Pro
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::expressionAfterForInOrOf(ParseNodeKind forHeadKind,
YieldHandling yieldHandling)
{
MOZ_ASSERT(forHeadKind == PNK_FORIN || forHeadKind == PNK_FOROF);
-
- return forHeadKind == PNK_FOROF
+ Node pn = forHeadKind == PNK_FOROF
? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
: expr(InAllowed, yieldHandling, TripledotProhibited);
+ return pn;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::declarationPattern(Node decl, TokenKind tt, BindData<ParseHandler>* data,
bool initialDeclaration, YieldHandling yieldHandling,
ParseNodeKind* forHeadKind,
Node* forInOrOfExpression)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB) ||
tokenStream.isCurrentTokenType(TOK_LC));
Node pattern;
{
pc->inDeclDestructuring = true;
- pattern = primaryExpr(yieldHandling, TripledotProhibited, tt);
+
+ // No possible error is required because we already know we're
+ // destructuring.
+ pattern = primaryExpr(yieldHandling, TripledotProhibited,
+ nullptr /* possibleError */ , tt);
pc->inDeclDestructuring = false;
}
if (!pattern)
return null();
if (initialDeclaration && forHeadKind) {
bool isForIn, isForOf;
if (!matchInOrOf(&isForIn, &isForOf))
@@ -4643,17 +4711,24 @@ Parser<ParseHandler>::declarationPattern
// See comment below for bindBeforeInitializer in the code that
// handles the non-destructuring case.
bool bindBeforeInitializer = handler.declarationIsVar(decl);
if (bindBeforeInitializer) {
if (!checkDestructuringPattern(data, pattern))
return null();
}
- MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
+ TokenKind token;
+ if (!tokenStream.getToken(&token, TokenStream::None))
+ return null();
+
+ if (token != TOK_ASSIGN) {
+ report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_DECL);
+ return null();
+ }
Node init = assignExpr(forHeadKind ? InProhibited : InAllowed,
yieldHandling, TripledotProhibited);
if (!init)
return null();
if (forHeadKind) {
// For for(;;) declarations, consistency with |for (;| parsing requires
@@ -6819,17 +6894,16 @@ Parser<ParseHandler>::tryStatement(Yield
Node catchName;
switch (tt) {
case TOK_LB:
case TOK_LC:
catchName = destructuringExpr(yieldHandling, &data, tt);
if (!catchName)
return null();
break;
-
case TOK_YIELD:
if (yieldHandling == YieldIsKeyword) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return null();
}
// Even if yield is *not* necessarily a keyword, we still must
// check its validity for legacy generators.
@@ -7454,46 +7528,79 @@ Parser<ParseHandler>::statement(YieldHan
// NOTE: default case handled in the ExpressionStatement section.
}
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
- TripledotHandling tripledotHandling, InvokedPrediction invoked)
-{
- Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, invoked);
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked)
+{
+ Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
+ possibleError, invoked);
if (!pn)
return null();
bool matched;
if (!tokenStream.matchToken(&matched, TOK_COMMA))
return null();
if (!matched)
return pn;
Node seq = handler.newCommaExpressionList(pn);
if (!seq)
return null();
while (true) {
- pn = assignExpr(inHandling, yieldHandling, tripledotHandling);
+ // Additional calls to assignExpr should not reuse the possibleError
+ // which had been passed into the function. Otherwise we would lose
+ // information needed to determine whether or not we're dealing with
+ // a non-recoverable situation.
+ PossibleError possibleErrorInner(*this);
+ pn = assignExpr(inHandling, yieldHandling, tripledotHandling,
+ &possibleErrorInner);
if (!pn)
return null();
+
+ // If we find an error here we should report it immedately instead of
+ // passing it back out of the function.
+ if (possibleErrorInner.hasError()) {
+
+ // We begin by checking for an outer pending error since it would
+ // have occurred first.
+ if (possibleError->checkForExprErrors())
+ possibleErrorInner.checkForExprErrors();
+ return null();
+ }
handler.addList(seq, pn);
if (!tokenStream.matchToken(&matched, TOK_COMMA))
return null();
if (!matched)
break;
}
return seq;
}
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ InvokedPrediction invoked)
+{
+ PossibleError possibleError(*this);
+ Node pn = expr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked);
+ if (!pn || !possibleError.checkForExprErrors())
+ return null();
+ return pn;
+}
+
static const JSOp ParseNodeKindToJSOp[] = {
JSOP_OR,
JSOP_AND,
JSOP_BITOR,
JSOP_BITXOR,
JSOP_BITAND,
JSOP_STRICTEQ,
JSOP_EQ,
@@ -7571,30 +7678,32 @@ Precedence(ParseNodeKind pnk) {
MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
return PrecedenceTable[pnk - PNK_BINOP_FIRST];
}
template <typename ParseHandler>
MOZ_ALWAYS_INLINE typename ParseHandler::Node
Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
- TripledotHandling tripledotHandling, InvokedPrediction invoked)
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked)
{
// Shift-reduce parser for the binary operator part of the JS expression
// syntax.
// Conceptually there's just one stack, a stack of pairs (lhs, op).
// It's implemented using two separate arrays, though.
Node nodeStack[PRECEDENCE_CLASSES];
ParseNodeKind kindStack[PRECEDENCE_CLASSES];
int depth = 0;
Node pn;
for (;;) {
- pn = unaryExpr(yieldHandling, tripledotHandling, invoked);
+ pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
if (!pn)
return pn;
// If a binary operator follows, consume it and compute the
// corresponding operator.
TokenKind tok;
if (!tokenStream.getToken(&tok))
return null();
@@ -7634,54 +7743,64 @@ Parser<ParseHandler>::orExpr1(InHandling
MOZ_ASSERT(depth == 0);
return pn;
}
template <typename ParseHandler>
MOZ_ALWAYS_INLINE typename ParseHandler::Node
Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
- TripledotHandling tripledotHandling, InvokedPrediction invoked)
-{
- Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked)
+{
+ Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, possibleError, invoked);
if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
return condition;
-
- Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
+ possibleError);
if (!thenExpr)
return null();
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
- Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+ Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited,
+ possibleError);
if (!elseExpr)
return null();
// Advance to the next token; the caller is responsible for interpreting it.
TokenKind ignored;
if (!tokenStream.getToken(&ignored))
return null();
return handler.newConditional(condition, thenExpr, elseExpr);
}
template <typename ParseHandler>
bool
-Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor)
+Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor,
+ PossibleError* possibleError)
{
MOZ_ASSERT(flavor != KeyedDestructuringAssignment,
"destructuring must use special checking/marking code, not "
"this method");
if (handler.isUnparenthesizedDestructuringPattern(target)) {
if (flavor == CompoundAssignment) {
report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
return false;
}
- return checkDestructuringPattern(nullptr, target);
+ bool isDestructuring = checkDestructuringPattern(nullptr, target);
+ // Here we've successfully distinguished between destructuring and
+ // an object literal. In the case where "CoverInitializedName"
+ // syntax was used there will be a pending error that needs clearing.
+ if (possibleError && isDestructuring)
+ possibleError->setResolved();
+ return isDestructuring;
}
// All other permitted targets are simple.
if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
return false;
if (handler.isPropertyAccess(target))
return true;
@@ -7699,19 +7818,22 @@ Parser<ParseHandler>::checkAndMarkAsAssi
MOZ_ASSERT(handler.isFunctionCall(target));
return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
- TripledotHandling tripledotHandling, InvokedPrediction invoked)
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked)
{
JS_CHECK_RECURSION(context, return null());
+ MOZ_ASSERT(!possibleError->hasError());
// It's very common at this point to have a "detectably simple" expression,
// i.e. a name/number/string token followed by one of the following tokens
// that obviously isn't part of an expression: , ; : ) ] }
//
// (In Parsemark this happens 81.4% of the time; in code with large
// numeric arrays, such as some Kraken benchmarks, it happens more often.)
//
@@ -7751,19 +7873,21 @@ Parser<ParseHandler>::assignExpr(InHandl
tokenStream.ungetToken();
// Save the tokenizer state in case we find an arrow function and have to
// rewind.
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
- Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
- if (!lhs)
- return null();
+ PossibleError possibleErrorInner(*this);
+ Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
+ if (!lhs) {
+ return null();
+ }
ParseNodeKind kind;
JSOp op;
switch (tokenStream.currentToken().type) {
case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break;
case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break;
case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break;
case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break;
@@ -7773,16 +7897,17 @@ Parser<ParseHandler>::assignExpr(InHandl
case TOK_RSHASSIGN: kind = PNK_RSHASSIGN; op = JSOP_RSH; break;
case TOK_URSHASSIGN: kind = PNK_URSHASSIGN; op = JSOP_URSH; break;
case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break;
case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break;
case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break;
case TOK_POWASSIGN: kind = PNK_POWASSIGN; op = JSOP_POW; break;
case TOK_ARROW: {
+
// A line terminator between ArrowParameters and the => should trigger a SyntaxError.
tokenStream.ungetToken();
TokenKind next = TOK_EOF;
if (!tokenStream.peekTokenSameLine(&next) || next != TOK_ARROW) {
report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
"expression", TokenKindToDesc(TOK_ARROW));
return null();
}
@@ -7835,35 +7960,53 @@ Parser<ParseHandler>::assignExpr(InHandl
return null();
tokenStream.addModifierException(TokenStream::NoneIsOperand);
}
return arrowFunc;
}
default:
MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
+ possibleErrorInner.transferErrorTo(possibleError);
tokenStream.ungetToken();
return lhs;
}
AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
- if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
+ if (!checkAndMarkAsAssignmentLhs(lhs, flavor, &possibleErrorInner))
+ return null();
+ if (!possibleErrorInner.checkForExprErrors())
return null();
bool saved = pc->inDeclDestructuring;
pc->inDeclDestructuring = false;
- Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
+ Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited,
+ possibleError);
pc->inDeclDestructuring = saved;
if (!rhs)
return null();
-
return handler.newAssignment(kind, lhs, rhs, pc, op);
}
template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ InvokedPrediction invoked)
+{
+ PossibleError possibleError(*this);
+ Node expr = assignExpr(inHandling, yieldHandling, tripledotHandling, &possibleError, invoked);
+ if (!expr || !possibleError.checkForExprErrors())
+ return null();
+
+ return expr;
+}
+
+
+template <typename ParseHandler>
bool
Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node,
FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
{
// Note that this method implements *only* a boolean test. Reporting an
// error for the various syntaxes that fail this, and warning for the
// various syntaxes that "pass" this but should not, occurs elsewhere.
@@ -7977,26 +8120,27 @@ Parser<ParseHandler>::checkAndMarkAsIncO
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
uint32_t begin)
{
- Node kid = unaryExpr(yieldHandling, TripledotProhibited);
- if (!kid)
+ PossibleError possibleError(*this);
+ Node kid = unaryExpr(yieldHandling, TripledotProhibited, &possibleError);
+ if (!kid || !possibleError.checkForExprErrors())
return null();
return handler.newUnary(kind, op, begin, kid);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
- InvokedPrediction invoked)
+ PossibleError* possibleError, InvokedPrediction invoked)
{
JS_CHECK_RECURSION(context, return null());
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return null();
uint32_t begin = pos().begin;
switch (tt) {
@@ -8018,59 +8162,59 @@ Parser<ParseHandler>::unaryExpr(YieldHan
//
// // Looks up the name, doesn't find it and so evaluates to
// // "undefined".
// assertEq(typeof nonExistentName, "undefined");
//
// // Evaluates expression, triggering a runtime ReferenceError for
// // the undefined name.
// typeof (1, nonExistentName);
- Node kid = unaryExpr(yieldHandling, TripledotProhibited);
+ Node kid = unaryExpr(yieldHandling, TripledotProhibited, possibleError);
if (!kid)
return null();
return handler.newTypeof(begin, kid);
}
case TOK_INC:
case TOK_DEC:
{
TokenKind tt2;
if (!tokenStream.getToken(&tt2, TokenStream::Operand))
return null();
- Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2, true);
+ Node pn2 = memberExpr(yieldHandling, TripledotProhibited, possibleError, tt2, true);
if (!pn2)
return null();
AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
if (!checkAndMarkAsIncOperand(pn2, flavor))
return null();
return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
JSOP_NOP,
begin,
pn2);
}
case TOK_DELETE: {
- Node expr = unaryExpr(yieldHandling, TripledotProhibited);
+ Node expr = unaryExpr(yieldHandling, TripledotProhibited, possibleError);
if (!expr)
return null();
// Per spec, deleting any unary expression is valid -- it simply
// returns true -- except for one case that is illegal in strict mode.
if (handler.isNameAnyParentheses(expr)) {
if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
return null();
pc->sc->setBindingsAccessedDynamically();
}
return handler.newDelete(begin, expr);
}
default: {
- Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+ Node pn = memberExpr(yieldHandling, tripledotHandling, possibleError, tt, /* allowCallSyntax = */ true,
invoked);
if (!pn)
return null();
/* Don't look across a newline boundary for a postfix incop. */
if (!tokenStream.peekTokenSameLine(&tt))
return null();
if (tt == TOK_INC || tt == TOK_DEC) {
@@ -8481,17 +8625,18 @@ Parser<ParseHandler>::checkAndMarkSuperS
return false;
pc->sc->markSuperScopeNeedsHomeObject();
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
- TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
+ PossibleError* possibleError, TokenKind tt,
+ bool allowCallSyntax, InvokedPrediction invoked)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
Node lhs;
JS_CHECK_RECURSION(context, return null());
/* Check for new expression first. */
@@ -8505,17 +8650,18 @@ Parser<ParseHandler>::memberExpr(YieldHa
lhs = newTarget;
} else {
lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW);
if (!lhs)
return null();
// Gotten by tryNewTarget
tt = tokenStream.currentToken().type;
- Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, false, PredictInvoked);
+ Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited,
+ possibleError, tt, false, PredictInvoked);
if (!ctorExpr)
return null();
handler.addList(lhs, ctorExpr);
bool matched;
if (!tokenStream.matchToken(&matched, TOK_LP))
return null();
@@ -8530,17 +8676,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
} else if (tt == TOK_SUPER) {
Node thisName = newThisName();
if (!thisName)
return null();
lhs = handler.newSuperBase(thisName, pos());
if (!lhs)
return null();
} else {
- lhs = primaryExpr(yieldHandling, tripledotHandling, tt, invoked);
+ lhs = primaryExpr(yieldHandling, tripledotHandling, possibleError, tt, invoked);
if (!lhs)
return null();
}
MOZ_ASSERT_IF(handler.isSuperBase(lhs), tokenStream.isCurrentTokenType(TOK_SUPER));
while (true) {
if (!tokenStream.getToken(&tt))
@@ -8561,17 +8707,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
if (!nextMember)
return null();
} else {
report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
return null();
}
} else if (tt == TOK_LB) {
- Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
+ Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited, possibleError);
if (!propExpr)
return null();
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
return null();
@@ -8686,16 +8832,29 @@ Parser<ParseHandler>::memberExpr(YieldHa
return null();
}
return lhs;
}
template <typename ParseHandler>
typename ParseHandler::Node
+Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
+ bool allowCallSyntax, InvokedPrediction invoked)
+{
+ PossibleError possibleError(*this);
+ Node pn = memberExpr(yieldHandling, tripledotHandling, &possibleError, tt,
+ allowCallSyntax, invoked);
+ if (!pn || !possibleError.checkForExprErrors())
+ return null();
+ return pn;
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
Parser<ParseHandler>::newName(PropertyName* name)
{
return handler.newName(name, pc->blockid(), pos(), context);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
@@ -8996,23 +9155,25 @@ Parser<ParseHandler>::propertyName(Yield
if (isGenerator) {
report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
return null();
}
*propType = PropertyType::Normal;
return propName;
}
- if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
+ if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) {
if (isGenerator) {
report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
return null();
}
tokenStream.ungetToken();
- *propType = PropertyType::Shorthand;
+ *propType = tt == TOK_ASSIGN ?
+ PropertyType::CoverInitializedName :
+ PropertyType::Shorthand;
return propName;
}
if (tt == TOK_LP) {
tokenStream.ungetToken();
*propType = isGenerator ? PropertyType::GeneratorMethod : PropertyType::Method;
return propName;
}
@@ -9043,25 +9204,26 @@ Parser<ParseHandler>::computedPropertyNa
if (!propname)
return null();
handler.setListFlag(literal, PNX_NONCONST);
return propname;
}
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling)
+Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
Node literal = handler.newObjectLiteral(pos().begin);
if (!literal)
return null();
bool seenPrototypeMutation = false;
+ bool seenCoverInitializedName = false;
RootedAtom propAtom(context);
for (;;) {
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
return null();
if (tt == TOK_RC)
break;
@@ -9110,16 +9272,53 @@ Parser<ParseHandler>::objectLiteral(Yiel
return null();
Node nameExpr = identifierName(yieldHandling);
if (!nameExpr)
return null();
if (!handler.addShorthand(literal, propName, nameExpr))
return null();
+ } else if (propType == PropertyType::CoverInitializedName) {
+ /*
+ * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand
+ * with default values, as per ES6 12.14.5 (2016/2/4)
+ */
+ if (!tokenStream.checkForKeyword(propAtom, nullptr))
+ return null();
+
+ Node lhs = identifierName(yieldHandling);
+ if (!lhs)
+ return null();
+
+ tokenStream.consumeKnownToken(TOK_ASSIGN);
+ Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!rhs)
+ return null();
+
+ Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP);
+ if (!propExpr)
+ return null();
+
+ if (!handler.addPropertyDefinition(literal, propName, propExpr))
+ return null();
+
+ if (!abortIfSyntaxParser())
+ return null();
+
+ if (!seenCoverInitializedName) {
+ seenCoverInitializedName = true;
+ // "shorthand default" or "CoverInitializedName" syntax is only
+ // valid in the case of destructuring. 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.
+ if (possibleError)
+ possibleError->setPending(ParseError, JSMSG_BAD_PROP_ID, false);
+ }
+
} else {
// FIXME: Implement ES6 function "name" property semantics
// (bug 883377).
RootedPropertyName funName(context);
switch (propType) {
case PropertyType::Getter:
case PropertyType::Setter:
funName = nullptr;
@@ -9209,33 +9408,34 @@ Parser<ParseHandler>::tryNewTarget(Node
newTarget = handler.newNewTarget(newHolder, targetHolder);
return !!newTarget;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
- TokenKind tt, InvokedPrediction invoked)
+ PossibleError* possibleError, TokenKind tt,
+ InvokedPrediction invoked)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
JS_CHECK_RECURSION(context, return null());
switch (tt) {
case TOK_FUNCTION:
return functionExpr(invoked);
case TOK_CLASS:
return classDefinition(yieldHandling, ClassExpression, NameRequired);
case TOK_LB:
return arrayInitializer(yieldHandling);
case TOK_LC:
- return objectLiteral(yieldHandling);
+ return objectLiteral(yieldHandling, possibleError);
case TOK_LP: {
TokenKind next;
if (!tokenStream.peekToken(&next, TokenStream::Operand))
return null();
if (next == TOK_RP) {
// Not valid expression syntax, but this is valid in an arrow function
@@ -9257,17 +9457,17 @@ Parser<ParseHandler>::primaryExpr(YieldH
}
if (next == TOK_FOR) {
uint32_t begin = pos().begin;
tokenStream.consumeKnownToken(next, TokenStream::Operand);
return generatorComprehension(begin);
}
- Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed);
+ Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
if (!expr)
return null();
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
handler.setEndPosition(expr, pos().end);
return handler.parenthesize(expr);
}
case TOK_TEMPLATE_HEAD:
@@ -9364,16 +9564,26 @@ Parser<ParseHandler>::primaryExpr(YieldH
"expression", TokenKindToDesc(tt));
return null();
}
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError)
+{
+ MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
+ return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
+}
+
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
return expr(inHandling, yieldHandling, tripledotHandling, PredictInvoked);
}
template <typename ParseHandler>
void
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -369,16 +369,17 @@ struct BindData;
class CompExprTransplanter;
enum VarContext { HoistVars, DontHoistVars };
enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody };
enum class PropertyType {
Normal,
Shorthand,
+ CoverInitializedName,
Getter,
GetterNoExpressionClosure,
Setter,
SetterNoExpressionClosure,
Method,
GeneratorMethod,
Constructor,
DerivedConstructor
@@ -410,16 +411,82 @@ class Parser : private JS::AutoGCRooter,
bool generateBlockId();
bool makeInnermostLexicalScope(StaticBlockScope& blockScope);
StmtInfoPC& operator*() { return stmt_; }
StmtInfoPC* operator->() { return &stmt_; }
operator StmtInfoPC*() { return &stmt_; }
};
+ /*
+ * A class for temporarily stashing errors while parsing continues.
+ *
+ * The ability to stash an error is useful for handling situations where we
+ * aren't able to verify that an error has occurred until later in the parse.
+ * For instance | ({x=1}) | is always parsed as an object literal with
+ * a SyntaxError, however, in the case where it is followed by '=>' we rewind
+ * and reparse it as a valid arrow function. Here a PossibleError would be
+ * set to 'pending' when the initial SyntaxError was encountered then 'resolved'
+ * just before rewinding the parser.
+ *
+ * When using PossibleError one should set a pending error at the location
+ * where an error occurs. From that point, the error may be resolved
+ * (invalidated) or left until the PossibleError is checked.
+ *
+ * Ex:
+ * PossibleError possibleError(*this);
+ * possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false);
+ * // A JSMSG_BAD_PROP_ID ParseError is reported, returns false.
+ * possibleError.checkForExprErrors();
+ *
+ * PossibleError possibleError(*this);
+ * possibleError.setPending(ParseError, JSMSG_BAD_PROP_ID, false);
+ * possibleError.setResolved();
+ * // Returns true, no error is reported.
+ * possibleError.checkForExprErrors();
+ *
+ * PossibleError possibleError(*this);
+ * // Returns true, no error is reported.
+ * possibleError.checkForExprErrors();
+ */
+ class MOZ_STACK_CLASS PossibleError
+ {
+ enum ErrorState { None, Pending };
+ ErrorState state_;
+
+ // Error reporting fields.
+ uint32_t offset_;
+ unsigned errorNumber_;
+ ParseReportKind reportKind_;
+ Parser<ParseHandler>& parser_;
+ bool strict_;
+
+ public:
+ explicit PossibleError(Parser<ParseHandler>& parser);
+
+ // Set a pending error.
+ void setPending(ParseReportKind kind, unsigned errorNumber, bool strict);
+
+ // Resolve any pending error.
+ void setResolved();
+
+ // Return true if an error is pending without reporting
+ bool hasError();
+
+ // If there is a pending error report it and return false, otherwise return
+ // true.
+ bool checkForExprErrors();
+
+ // Pass pending errors between possible error instances. This is useful
+ // for extending the lifetime of a pending error beyond the scope of
+ // the PossibleError where it was initially set (keeping in mind that
+ // PossibleError is a MOZ_STACK_CLASS).
+ void transferErrorTo(PossibleError* other);
+ };
+
public:
ExclusiveContext* const context;
LifoAlloc& alloc;
TokenStream tokenStream;
LifoAlloc::Mark tempPoolMark;
/* list of parsed objects for GC tracing */
@@ -760,35 +827,53 @@ class Parser : private JS::AutoGCRooter,
// |forHeadKind|.
bool initializerInNameDeclaration(Node decl, Node binding, Handle<PropertyName*> name,
BindData<ParseHandler>* data, bool initialDeclaration,
YieldHandling yieldHandling, ParseNodeKind* forHeadKind,
Node* forInOrOfExpression);
Node expr(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
InvokedPrediction invoked = PredictUninvoked);
+ Node expr(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ InvokedPrediction invoked = PredictUninvoked);
+ Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
+ InvokedPrediction invoked = PredictUninvoked);
Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
InvokedPrediction invoked = PredictUninvoked);
Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
Node yieldExpression(InHandling inHandling);
Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
InvokedPrediction invoked = PredictUninvoked);
Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
- InvokedPrediction invoked = PredictUninvoked);
+ PossibleError* possibleError,
+ InvokedPrediction invoked = PredictUninvoked);
Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ PossibleError* possibleError,
InvokedPrediction invoked = PredictUninvoked);
+ Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ PossibleError* possibleError, TokenKind tt,
+ bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
- Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
+ Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ PossibleError* possibleError, TokenKind tt,
InvokedPrediction invoked = PredictUninvoked);
Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ PossibleError* possibleError);
+ Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling);
bool tryNewTarget(Node& newTarget);
bool checkAndMarkSuperScope();
Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
HandlePropertyName funName);
@@ -849,17 +934,18 @@ class Parser : private JS::AutoGCRooter,
PlainAssignment,
CompoundAssignment,
KeyedDestructuringAssignment,
IncrementAssignment,
DecrementAssignment,
ForInOrOfTarget
};
- bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
+ bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor,
+ PossibleError* possibleError=nullptr);
bool matchInOrOf(bool* isForInp, bool* isForOfp);
bool checkFunctionArguments();
bool defineFunctionThis();
Node newThisName();
bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
@@ -904,17 +990,17 @@ class Parser : private JS::AutoGCRooter,
Node pushLetScope(Handle<StaticBlockScope*> blockScope, AutoPushStmtInfoPC& stmt);
bool noteNameUse(HandlePropertyName name, Node pn);
Node propertyName(YieldHandling yieldHandling, Node propList,
PropertyType* propType, MutableHandleAtom propAtom);
Node computedPropertyName(YieldHandling yieldHandling, Node literal);
Node arrayInitializer(YieldHandling yieldHandling);
Node newRegExp();
- Node objectLiteral(YieldHandling yieldHandling);
+ Node objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError);
enum PrepareLexicalKind {
PrepareLet,
PrepareConst,
PrepareFunction
};
bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos);
bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
--- a/js/src/jit-test/tests/basic/destructuring-default.js
+++ b/js/src/jit-test/tests/basic/destructuring-default.js
@@ -1,27 +1,33 @@
load(libdir + 'asserts.js');
load(libdir + 'eqArrayHelper.js');
var arrayPattern = '[a = 1, b = 2, c = 3, d = 4, e = 5, f = 6]';
var objectPattern = '{0: a = 1, 1: b = 2, 2: c = 3, 3: d = 4, 4: e = 5, 5: f = 6}';
+var objectPatternShorthand = '{a = 1, b = 2, c = 3, d = 4, e = 5, f = 6}';
var nestedPattern = '{a: a = 1, b: [b = 2] = [], c: {c: [c]} = {c: [3]}, d: {d, e} = {d: 4, e: 5}, f: f = 6}';
function testAll(fn) {
assertEqArray(fn(arrayPattern, []), [1, 2, 3, 4, 5, 6]);
assertEqArray(fn(arrayPattern, [2, 3, 4, 5, 6, 7, 8, 9]), [2, 3, 4, 5, 6, 7]);
assertEqArray(fn(arrayPattern, [undefined, 0, false, null, "", undefined]), [1, 0, false, null, "", 6]);
assertEqArray(fn(arrayPattern, [0, false]), [0, false, 3, 4, 5, 6]);
assertEqArray(fn(objectPattern, []), [1, 2, 3, 4, 5, 6]);
assertEqArray(fn(objectPattern, [2, 3, 4, 5, 6, 7, 8, 9]), [2, 3, 4, 5, 6, 7]);
assertEqArray(fn(objectPattern, [undefined, 0, false, null, "", undefined]), [1, 0, false, null, "", 6]);
assertEqArray(fn(objectPattern, [0, false]), [0, false, 3, 4, 5, 6]);
+ assertEqArray(fn(objectPatternShorthand, {}), [1, 2, 3, 4, 5, 6]);
+ assertEqArray(fn(objectPatternShorthand, {a: 2, b: 3, c: 4, d: 5, e: 6, f: 7, g: 8, h: 9}), [2, 3, 4, 5, 6, 7]);
+ assertEqArray(fn(objectPatternShorthand, {a: undefined, b: 0, c: false, d: null, e: "", f: undefined}),
+ [1, 0, false, null, "", 6]);
+ assertEqArray(fn(objectPatternShorthand, {a: 0, b: false}), [0, false, 3, 4, 5, 6]);
assertEqArray(fn(nestedPattern, {}), [1, 2, 3, 4, 5, 6]);
assertEqArray(fn(nestedPattern, {a: 2, b: [], c: undefined}), [2, 2, 3, 4, 5, 6]);
assertEqArray(fn(nestedPattern, {a: undefined, b: [3], c: {c: [4]}}), [1, 3, 4, 4, 5, 6]);
assertEqArray(fn(nestedPattern, {a: undefined, b: [3], c: {c: [4]}, d: {d: 5, e: 6}}), [1, 3, 4, 5, 6, 6]);
}
function testVar(pattern, input) {
return new Function('input',
@@ -170,10 +176,14 @@ if (defaultsSupportedInForVar) {
b = undefined;
for (var {1: b = 10} in " ") {}
assertEq(b, 10);
b = undefined;
for (let {1: c = 10} in " ") { b = c; }
assertEq(b, 10);
+
+ b = undefined;
+ for (let {c = 10} in " ") { b = c; }
+ assertEq(b, 10);
`)();
}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Object/destructuring-shorthand-defaults.js
@@ -0,0 +1,102 @@
+/* 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/. */
+
+// Ensure that the syntax used in shorthand destructuring with defaults
+// e.g. |{x=1, y=2} = {}| properly raises a syntax error within an object
+// literal. As per ES6 12.2.6 Object Initializer, "NOTE 3."
+
+const SYNTAX_ERROR_STMTS = [
+ // expressions
+ "({x={}={}}),",
+ "({y={x={}={}={}={}={}={}={}={}}={}}),",
+ "({a=1, b=2, c=3, x=({}={})}),",
+ "({x=1, y={z={1}}})",
+ "({x=1} = {y=1});",
+ "({x: y={z=1}}={})",
+ "({x=1}),",
+ "({z=1}={}, {w=2}, {e=3})=>{};",
+ "({z={x=1}})=>{};",
+ "({x = ({y=1}) => y})",
+ "(({x=1})) => x",
+ // declarations
+ "let o = {x=1};",
+ "var j = {x=1};",
+ "var j = {x={y=1}}={};",
+ "const z = {x=1};",
+ "const z = {x={y=1}}={};",
+ "const {x=1};",
+ "const {x={y=33}}={};",
+ "var {x=1};",
+ "let {x=1};",
+ "let x, y, {z=1}={}, {w=2}, {e=3};",
+ // array initialization
+ "[{x=1, y = ({z=2} = {})}];",
+ // try/catch
+ "try {throw 'a';} catch ({x={y=1}}) {}",
+ // if/else
+ "if ({k: 1, x={y=2}={}}) {}",
+ "if (false) {} else if (true) { ({x=1}) }",
+ // switch
+ "switch ('c') { case 'c': ({x=1}); }",
+ // for
+ "for ({x=1}; 1;) {1}",
+ "for ({x={y=2}}; 1;) {1}",
+ "for (var x = 0; x < 2; x++) { ({x=1, y=2}) }",
+ "for (let x=1;{x=1};){}",
+ "for (let x=1;{x={y=2}};){}",
+ "for (let x=1;1;{x=1}){}",
+ "for (let x=1;1;{x={y=2}}){}",
+ // while
+ "while ({x=1}) {1};",
+ "while ({x={y=2}}={}) {1};",
+ // with
+ "with ({x=1}) {};",
+ "with ({x={y=3}={}}) {};",
+ "with (Math) { ({x=1}) };"
+]
+
+for (var stmt of SYNTAX_ERROR_STMTS) {
+ assertThrowsInstanceOf(() => {
+ eval(stmt);
+ }, SyntaxError);
+}
+
+const REFERENCE_ERROR_STMTS = [
+ "({x} += {});",
+ "({x = 1}) = {x: 2};",
+]
+
+for (var stmt of REFERENCE_ERROR_STMTS) {
+ assertThrowsInstanceOf(() => {
+ eval(stmt);
+ }, ReferenceError);
+}
+
+// A few tricky but acceptable cases:
+// see https://bugzilla.mozilla.org/show_bug.cgi?id=932080#c2
+
+assertEq((({a = 0}) => a)({}), 0);
+assertEq((({a = 0} = {}) => a)({}), 0);
+assertEq((({a = 0} = {}) => a)({a: 1}), 1);
+
+{
+ let x, y;
+ ({x=1} = {});
+ assertEq(x, 1);
+ ({x=1} = {x: 4});
+ assertEq(x, 4);
+ ({x=1, y=2} = {})
+ assertEq(x, 1);
+ assertEq(y, 2);
+}
+
+{
+ let {x={i=1, j=2}={}}={};
+ assertDeepEq(x, ({}));
+ assertEq(i, 1);
+ assertEq(j, 2);
+}
+
+if (typeof reportCompare == "function")
+ reportCompare(true, true);