#
# Manual backport of optchain support, preliminary part:
#
# bug 1566143 part 1
# bug 1566143 part 2
#
diff -Nrup mozilla-OLD/js/src/frontend/BytecodeEmitter.cpp mozilla/js/src/frontend/BytecodeEmitter.cpp
--- mozilla-OLD/js/src/frontend/BytecodeEmitter.cpp 2022-01-10 07:20:08.134341896 +0300
+++ mozilla/js/src/frontend/BytecodeEmitter.cpp 2022-01-10 07:21:07.212924429 +0300
@@ -9604,6 +9604,42 @@ BytecodeEmitter::emitPipeline(ParseNode*
return true;
}
+ParseNode* BytecodeEmitter::getCoordNode(ParseNode* pn,
+ ParseNode* calleeNode,
+ ParseNode* argsList) {
+ ParseNode* coordNode = pn;
+ if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) || pn->isOp(JSOP_FUNCALL) ||
+ pn->isOp(JSOP_FUNAPPLY)) {
+ // Default to using the location of the `(` itself.
+ // obj[expr]() // expression
+ // ^ // column coord
+ coordNode = argsList;
+
+ switch (calleeNode->getKind()) {
+ case ParseNodeKind::Dot:
+ // Use the position of a property access identifier.
+ //
+ // obj().aprop() // expression
+ // ^ // column coord
+ //
+ // Note: Because of the constant folding logic in FoldElement,
+ // this case also applies for constant string properties.
+ //
+ // obj()['aprop']() // expression
+ // ^ // column coord
+ coordNode = calleeNode->pn_right;
+ break;
+ case ParseNodeKind::Name:
+ // Use the start of callee names.
+ coordNode = calleeNode;
+ break;
+ default:
+ break;
+ }
+ }
+ return coordNode;
+}
+
bool
BytecodeEmitter::emitArguments(ParseNode* pn, bool callop, bool spread)
{
@@ -9745,36 +9781,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode
}
}
- ParseNode* coordNode = pn;
- if (pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) || pn->isOp(JSOP_FUNCALL) ||
- pn->isOp(JSOP_FUNAPPLY)) {
- // Default to using the location of the `(` itself.
- // obj[expr]() // expression
- // ^ // column coord
- coordNode = pn_args;
-
- switch (pn_callee->getKind()) {
- case ParseNodeKind::Dot:
- // Use the position of a property access identifier.
- //
- // obj().aprop() // expression
- // ^ // column coord
- //
- // Note: Because of the constant folding logic in FoldElement,
- // this case also applies for constant string properties.
- //
- // obj()['aprop']() // expression
- // ^ // column coord
- coordNode = pn_callee->pn_right;
- break;
- case ParseNodeKind::Name:
- // Use the start of callee names.
- coordNode = pn_callee;
- break;
- default:
- break;
- }
- }
+ ParseNode* coordNode = getCoordNode(pn, pn_callee, pn_args);
if (!spread) {
if (pn->getOp() == JSOP_CALL && valueUsage == ValueUsage::IgnoreValue) {
diff -Nrup mozilla-OLD/js/src/frontend/BytecodeEmitter.h mozilla/js/src/frontend/BytecodeEmitter.h
--- mozilla-OLD/js/src/frontend/BytecodeEmitter.h 2022-01-10 07:20:08.135341889 +0300
+++ mozilla/js/src/frontend/BytecodeEmitter.h 2022-01-10 07:21:07.213924422 +0300
@@ -818,6 +818,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
bool isRestParameter(ParseNode* pn);
+ MOZ_MUST_USE ParseNode* getCoordNode(ParseNode* pn,
+ ParseNode* calleeNode,
+ ParseNode* argsList);
MOZ_MUST_USE bool emitArguments(ParseNode* pn, bool callop, bool spread);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
diff -Nrup mozilla-OLD/js/src/frontend/FoldConstants.cpp mozilla/js/src/frontend/FoldConstants.cpp
--- mozilla-OLD/js/src/frontend/FoldConstants.cpp 2022-01-10 07:07:24.305742743 +0300
+++ mozilla/js/src/frontend/FoldConstants.cpp 2022-01-10 07:21:07.214924415 +0300
@@ -315,6 +315,7 @@ ContainsHoistedDeclaration(JSContext* cx
case ParseNodeKind::DeleteName:
case ParseNodeKind::DeleteProp:
case ParseNodeKind::DeleteElem:
+ case ParseNodeKind::DeleteOptionalChain:
case ParseNodeKind::DeleteExpr:
case ParseNodeKind::Pos:
case ParseNodeKind::Neg:
diff -Nrup mozilla-OLD/js/src/frontend/FullParseHandler.h mozilla/js/src/frontend/FullParseHandler.h
--- mozilla-OLD/js/src/frontend/FullParseHandler.h 2022-01-10 07:07:24.306742735 +0300
+++ mozilla/js/src/frontend/FullParseHandler.h 2022-01-10 07:21:07.214924415 +0300
@@ -179,6 +179,18 @@ class FullParseHandler
if (expr->isKind(ParseNodeKind::Elem))
return newUnary(ParseNodeKind::DeleteElem, begin, expr);
+ if (expr->isKind(ParseNodeKind::OptionalChain)) {
+ ParseNode* kid = expr->pn_kid;
+ // Handle property deletion explictly. OptionalCall is handled
+ // via DeleteExpr.
+ if (kid->isKind(ParseNodeKind::Dot) ||
+ kid->isKind(ParseNodeKind::OptionalDot) ||
+ kid->isKind(ParseNodeKind::Elem) ||
+ kid->isKind(ParseNodeKind::OptionalElem)) {
+ return newUnary(ParseNodeKind::DeleteOptionalChain, begin, kid);
+ }
+ }
+
return newUnary(ParseNodeKind::DeleteExpr, begin, expr);
}
@@ -289,6 +301,10 @@ class FullParseHandler
return new_<BinaryNode>(ParseNodeKind::Call, JSOP_CALL, callee, args);
}
+ ParseNode* newOptionalCall(ParseNode* callee, ParseNode* args) {
+ return new_<BinaryNode>(ParseNodeKind::OptionalCall, JSOP_CALL, callee, args);
+ }
+
ParseNode* newArguments(const TokenPos& pos) {
return new_<ListNode>(ParseNodeKind::Arguments, JSOP_NOP, pos);
}
@@ -438,6 +454,11 @@ class FullParseHandler
return new_<UnaryNode>(ParseNodeKind::Await, pos, value);
}
+ ParseNode* newOptionalChain(uint32_t begin, Node value) {
+ TokenPos pos(begin, value->pn_pos.end);
+ return new_<UnaryNode>(ParseNodeKind::OptionalChain, pos, value);
+ }
+
// Statements
ParseNode* newStatementList(const TokenPos& pos) {
@@ -685,6 +706,14 @@ class FullParseHandler
return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
}
+ ParseNode* newOptionalPropertyAccess(ParseNode* expr, ParseNode* key) {
+ return new_<OptionalPropertyAccess>(expr, key, expr->pn_pos.begin, key->pn_pos.end);
+ }
+
+ ParseNode* newOptionalPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
+ return new_<OptionalPropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
+ }
+
bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
ParseNode* catchpn;
if (catchName) {
diff -Nrup mozilla-OLD/js/src/frontend/ParseNode.h mozilla/js/src/frontend/ParseNode.h
--- mozilla-OLD/js/src/frontend/ParseNode.h 2022-01-10 07:07:24.306742735 +0300
+++ mozilla/js/src/frontend/ParseNode.h 2022-01-10 07:21:07.215924408 +0300
@@ -37,6 +37,10 @@ class ObjectBox;
F(PropertyName) \
F(Dot) \
F(Elem) \
+ F(OptionalDot) \
+ F(OptionalChain) \
+ F(OptionalElem) \
+ F(OptionalCall) \
F(Array) \
F(Elision) \
F(StatementList) \
@@ -79,6 +83,7 @@ class ObjectBox;
F(DeleteName) \
F(DeleteProp) \
F(DeleteElem) \
+ F(DeleteOptionalChain) \
F(DeleteExpr) \
F(Try) \
F(Catch) \
@@ -1197,31 +1202,32 @@ class RegExpLiteral : public NullaryNode
}
};
-class PropertyAccess : public BinaryNode
+class PropertyAccessBase : public BinaryNode
{
public:
/*
* PropertyAccess nodes can have any expression/'super' as left-hand
* side, but the name must be a ParseNodeKind::PropertyName node.
*/
- PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
- : BinaryNode(ParseNodeKind::Dot, JSOP_NOP, TokenPos(begin, end), lhs, name)
+ PropertyAccessBase(ParseNodeKind kind, ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
+ : BinaryNode(kind, JSOP_NOP, TokenPos(begin, end), lhs, name)
{
MOZ_ASSERT(lhs != nullptr);
MOZ_ASSERT(name != nullptr);
}
+ ParseNode& expression() const {
+ return *pn_u.binary.left;
+ }
+
static bool test(const ParseNode& node) {
- bool match = node.isKind(ParseNodeKind::Dot);
+ bool match = node.isKind(ParseNodeKind::Dot) ||
+ node.isKind(ParseNodeKind::OptionalDot);
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
MOZ_ASSERT_IF(match, node.pn_right->isKind(ParseNodeKind::PropertyName));
return match;
}
- ParseNode& expression() const {
- return *pn_u.binary.left;
- }
-
PropertyName& name() const {
return *pn_u.binary.right->pn_atom->asPropertyName();
}
@@ -1232,18 +1238,51 @@ class PropertyAccess : public BinaryNode
}
};
-class PropertyByValue : public ParseNode
+class PropertyAccess : public PropertyAccessBase
{
public:
- PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end)
- : ParseNode(ParseNodeKind::Elem, JSOP_NOP, PN_BINARY, TokenPos(begin, end))
+ PropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
+ : PropertyAccessBase(ParseNodeKind::Dot, lhs, name, begin, end) {
+ MOZ_ASSERT(lhs);
+ MOZ_ASSERT(name);
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(ParseNodeKind::Dot);
+ MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>());
+ return match;
+ }
+};
+
+class OptionalPropertyAccess : public PropertyAccessBase {
+ public:
+ OptionalPropertyAccess(ParseNode* lhs, ParseNode* name, uint32_t begin, uint32_t end)
+ : PropertyAccessBase(ParseNodeKind::OptionalDot, lhs, name, begin, end) {
+ MOZ_ASSERT(lhs);
+ MOZ_ASSERT(name);
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(ParseNodeKind::OptionalDot);
+ MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>());
+ return match;
+ }
+};
+
+class PropertyByValueBase : public ParseNode
+{
+ public:
+ PropertyByValueBase(ParseNodeKind kind, ParseNode* lhs, ParseNode* propExpr,
+ uint32_t begin, uint32_t end)
+ : ParseNode(kind, JSOP_NOP, PN_BINARY, TokenPos(begin, end))
{
pn_u.binary.left = lhs;
pn_u.binary.right = propExpr;
}
static bool test(const ParseNode& node) {
- bool match = node.isKind(ParseNodeKind::Elem);
+ bool match = node.isKind(ParseNodeKind::Elem) ||
+ node.isKind(ParseNodeKind::OptionalElem);
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
return match;
}
@@ -1253,6 +1292,30 @@ class PropertyByValue : public ParseNode
}
};
+class PropertyByValue : public PropertyByValueBase {
+ public:
+ PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end)
+ : PropertyByValueBase(ParseNodeKind::Elem, lhs, propExpr, begin, end) {}
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(ParseNodeKind::Elem);
+ MOZ_ASSERT_IF(match, node.is<PropertyByValueBase>());
+ return match;
+ }
+};
+
+class OptionalPropertyByValue : public PropertyByValueBase {
+ public:
+ OptionalPropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end)
+ : PropertyByValueBase(ParseNodeKind::OptionalElem, lhs, propExpr, begin, end) {}
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(ParseNodeKind::OptionalElem);
+ MOZ_ASSERT_IF(match, node.is<PropertyByValueBase>());
+ return match;
+ }
+};
+
/*
* A CallSiteNode represents the implicit call site object argument in a TaggedTemplate.
*/
diff -Nrup mozilla-OLD/js/src/frontend/Parser.cpp mozilla/js/src/frontend/Parser.cpp
--- mozilla-OLD/js/src/frontend/Parser.cpp 2022-01-10 07:07:24.309742714 +0300
+++ mozilla/js/src/frontend/Parser.cpp 2022-01-10 07:21:07.218924387 +0300
@@ -8413,6 +8413,107 @@ Parser<ParseHandler, CharT>::unaryOpExpr
template <class ParseHandler, typename CharT>
typename ParseHandler::Node
+Parser<ParseHandler, CharT>::optionalExpr(YieldHandling yieldHandling,
+ TripledotHandling tripledotHandling,
+ TokenKind tt, bool allowCallSyntax /* = true */,
+ PossibleError* possibleError /* = nullptr */,
+ InvokedPrediction invoked /* = PredictUninvoked */)
+{
+ if (!CheckRecursionLimit(context)) {
+ return null();
+ }
+
+ uint32_t begin = pos().begin;
+
+ Node lhs = memberExpr(yieldHandling, tripledotHandling, tt,
+ /* allowCallSyntax = */ true, possibleError, invoked);
+ if (!lhs) {
+ return null();
+ }
+
+ if (!tokenStream.peekToken(&tt, TokenStream::None)) {
+ return null();
+ }
+
+ if (tt == TOK_EOF || tt != TOK_OPTCHAIN) {
+ return lhs;
+ }
+
+ while (true) {
+ if (!tokenStream.getToken(&tt)) {
+ return null();
+ }
+
+ if (tt == TOK_EOF) {
+ break;
+ }
+
+ Node nextMember;
+ if (tt == TOK_OPTCHAIN) {
+ if (!tokenStream.getToken(&tt)) {
+ return null();
+ }
+ if (TokenKindIsPossibleIdentifierName(tt)) {
+ nextMember = memberPropertyAccess(lhs, OptionalKind::Optional);
+ if (!nextMember) {
+ return null();
+ }
+ } else if (tt == TOK_LB) {
+ nextMember = memberElemAccess(lhs, yieldHandling, OptionalKind::Optional);
+ if (!nextMember) {
+ return null();
+ }
+ } else if (tt == TOK_LP) {
+ nextMember = memberCall(tt, lhs, yieldHandling, possibleError, OptionalKind::Optional);
+ if (!nextMember) {
+ return null();
+ }
+ } else {
+ error(JSMSG_NAME_AFTER_DOT);
+ return null();
+ }
+ } else if (tt == TOK_DOT) {
+ if (!tokenStream.getToken(&tt)) {
+ return null();
+ }
+ if (TokenKindIsPossibleIdentifierName(tt)) {
+ nextMember = memberPropertyAccess(lhs);
+ if (!nextMember) {
+ return null();
+ }
+ } else {
+ error(JSMSG_NAME_AFTER_DOT);
+ return null();
+ }
+ } else if (tt == TOK_LB) {
+ nextMember = memberElemAccess(lhs, yieldHandling);
+ if (!nextMember) {
+ return null();
+ }
+ } else if (allowCallSyntax && tt == TOK_LP) {
+ nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
+ if (!nextMember) {
+ return null();
+ }
+ } else if (tt == TOK_TEMPLATE_HEAD ||
+ tt == TOK_NO_SUBS_TEMPLATE) {
+ error(JSMSG_BAD_OPTIONAL_TEMPLATE);
+ return null();
+ } else {
+ anyChars.ungetToken();
+ break;
+ }
+
+ if (nextMember) {
+ lhs = nextMember;
+ }
+ }
+
+ return handler.newOptionalChain(begin, lhs);
+}
+
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
PossibleError* possibleError /* = nullptr */,
@@ -8508,8 +8609,8 @@ Parser<ParseHandler, CharT>::unaryExpr(Y
MOZ_FALLTHROUGH;
default: {
- Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
- possibleError, invoked);
+ Node expr = optionalExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+ possibleError, invoked);
if (!expr)
return null();
@@ -8955,6 +9056,18 @@ Parser<ParseHandler, CharT>::memberExpr(
if (!ctorExpr)
return null();
+ // If we have encountered an optional chain, in the form of `new
+ // ClassName?.()` then we need to throw, as this is disallowed by the
+ // spec.
+ bool optionalToken;
+ if (!tokenStream.matchToken(&optionalToken, TOK_OPTCHAIN)) {
+ return null();
+ }
+ if (optionalToken) {
+ errorAt(newBegin, JSMSG_BAD_NEW_OPTIONAL);
+ return null();
+ }
+
bool matched;
if (!tokenStream.matchToken(&matched, TOK_LP))
return null();
@@ -9003,17 +9116,7 @@ Parser<ParseHandler, CharT>::memberExpr(
if (!tokenStream.getToken(&tt))
return null();
if (TokenKindIsPossibleIdentifierName(tt)) {
- PropertyName* field = anyChars.currentName();
- if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
- error(JSMSG_BAD_SUPERPROP, "property");
- return null();
- }
-
- Node name = handler.newPropertyName(field, pos());
- if (!name)
- return null();
-
- nextMember = handler.newPropertyAccess(lhs, name);
+ nextMember = memberPropertyAccess(lhs);
if (!nextMember)
return null();
} else {
@@ -9021,17 +9124,7 @@ Parser<ParseHandler, CharT>::memberExpr(
return null();
}
} else if (tt == TOK_LB) {
- Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
- if (!propExpr)
- return null();
-
- MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
-
- if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
- error(JSMSG_BAD_SUPERPROP, "member");
- return null();
- }
- nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
+ nextMember = memberElemAccess(lhs, yieldHandling);
if (!nextMember)
return null();
} else if ((allowCallSyntax && tt == TOK_LP) ||
@@ -9049,106 +9142,14 @@ Parser<ParseHandler, CharT>::memberExpr(
return null();
}
- // Despite the fact that it's impossible to have |super()| in a
- // generator, we still inherit the yieldHandling of the
- // memberExpression, per spec. Curious.
- bool isSpread = false;
- Node args = argumentList(yieldHandling, &isSpread);
- if (!args)
- return null();
-
- nextMember = handler.newSuperCall(lhs, args);
- if (!nextMember)
- return null();
-
- if (isSpread)
- handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
-
- Node thisName = newThisName();
- if (!thisName)
- return null();
-
- nextMember = handler.newSetThis(thisName, nextMember);
+ nextMember = memberSuperCall(lhs, yieldHandling);
if (!nextMember)
return null();
} else {
- if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
- error(JSMSG_SELFHOSTED_METHOD_CALL);
+ nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
+ if (!nextMember) {
return null();
}
-
- JSOp op = JSOP_CALL;
- bool maybeAsyncArrow = false;
- 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.isEvalName(lhs, context)) {
- // Select the right EVAL op and flag pc as having a
- // direct eval.
- op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
- pc->sc()->setBindingsAccessedDynamically();
- pc->sc()->setHasDirectEval();
-
- // In non-strict mode code, direct calls to eval can
- // add variables to the call object.
- if (pc->isFunctionBox() && !pc->sc()->strict())
- pc->functionBox()->setHasExtensibleScope();
-
- // If we're in a method, mark the method as requiring
- // support for 'super', since direct eval code can use
- // it. (If we're not in a method, that's fine, so
- // ignore the return value.)
- checkAndMarkSuperScope();
- }
- }
-
- if (tt == TOK_LP) {
- bool isSpread = false;
- PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
- Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError);
- if (!args)
- return null();
- if (isSpread) {
- if (op == JSOP_EVAL)
- op = JSOP_SPREADEVAL;
- else if (op == JSOP_STRICTEVAL)
- op = JSOP_STRICTSPREADEVAL;
- else
- op = JSOP_SPREADCALL;
- }
-
- nextMember = handler.newCall(lhs, args);
- if (!nextMember)
- return null();
- } else {
- Node args = handler.newArguments(pos());
- if (!args)
- return null();
-
- if (!taggedTemplate(yieldHandling, args, tt))
- return null();
-
- nextMember = handler.newTaggedTemplate(lhs, args);
- if (!nextMember)
- return null();
- }
- handler.setOp(nextMember, op);
}
} else {
anyChars.ungetToken();
@@ -9183,6 +9184,193 @@ Parser<ParseHandler, CharT>::newName(Pro
}
template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
+Parser<ParseHandler, CharT>::memberPropertyAccess(Node lhs,
+ OptionalKind optionalKind /* = OptionalKind::NonOptional */)
+{
+ MOZ_ASSERT(TokenKindIsPossibleIdentifierName(anyChars.currentToken().type));
+ PropertyName* field = anyChars.currentName();
+ if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
+ error(JSMSG_BAD_SUPERPROP, "property");
+ return null();
+ }
+
+ Node name = handler.newPropertyName(field, pos());
+ if (!name) {
+ return null();
+ }
+
+ if (optionalKind == OptionalKind::Optional) {
+ return handler.newOptionalPropertyAccess(lhs, name);
+ }
+ return handler.newPropertyAccess(lhs, name);
+}
+
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
+Parser<ParseHandler, CharT>::memberElemAccess(Node lhs, YieldHandling yieldHandling,
+ OptionalKind optionalKind /* = OptionalKind::NonOptional */)
+{
+ MOZ_ASSERT(anyChars.currentToken().type == TokenKind::LeftBracket);
+ Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
+ if (!propExpr) {
+ return null();
+ }
+
+ MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_IN_INDEX);
+
+ if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
+ error(JSMSG_BAD_SUPERPROP, "member");
+ return null();
+ }
+ if (optionalKind == OptionalKind::Optional) {
+ return handler.newOptionalPropertyByValue(lhs, propExpr, pos().end);
+ }
+ return handler.newPropertyByValue(lhs, propExpr, pos().end);
+}
+
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
+Parser<ParseHandler, CharT>::memberSuperCall(Node lhs, YieldHandling yieldHandling)
+{
+ MOZ_ASSERT(anyChars.currentToken().type == TOK_LP);
+ // Despite the fact that it's impossible to have |super()| in a
+ // generator, we still inherit the yieldHandling of the
+ // memberExpression, per spec. Curious.
+ bool isSpread = false;
+ Node args = argumentList(yieldHandling, &isSpread);
+ if (!args) {
+ return null();
+ }
+
+ Node nextMember = handler.newSuperCall(lhs, args);
+ if (!nextMember){
+ return null();
+ }
+
+ if (isSpread) {
+ handler.setOp(nextMember, JSOP_SPREADSUPERCALL);
+ }
+
+ Node thisName = newThisName();
+ if (!thisName) {
+ return null();
+ }
+
+ return handler.newSetThis(thisName, nextMember);
+}
+
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
+Parser<ParseHandler, CharT>::memberCall(TokenKind tt, Node lhs, YieldHandling yieldHandling,
+ PossibleError* possibleError /* = nullptr */,
+ OptionalKind optionalKind /* = OptionalKind::NonOptional */)
+{
+ if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
+ error(JSMSG_SELFHOSTED_METHOD_CALL);
+ return null();
+ }
+
+ MOZ_ASSERT(tt == TOK_LP || tt == TOK_TEMPLATE_HEAD || tt == TOK_NO_SUBS_TEMPLATE,
+ "Unexpected token kind for member call");
+
+ JSOp op = JSOP_CALL;
+ bool maybeAsyncArrow = false;
+ 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.isEvalName(lhs, context)) {
+ // Select the right Eval op and flag pc as having a
+ // direct eval.
+ op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
+ pc->sc()->setBindingsAccessedDynamically();
+ pc->sc()->setHasDirectEval();
+
+ // In non-strict mode code, direct calls to eval can
+ // add variables to the call object.
+ if (pc->isFunctionBox() && !pc->sc()->strict()) {
+ pc->functionBox()->setHasExtensibleScope();
+ }
+
+ // If we're in a method, mark the method as requiring
+ // support for 'super', since direct eval code can use
+ // it. (If we're not in a method, that's fine, so
+ // ignore the return value.)
+ checkAndMarkSuperScope();
+ }
+ }
+
+ if (tt == TOK_LP) {
+ bool isSpread = false;
+ PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
+ Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError);
+ if (!args) {
+ return null();
+ }
+ if (isSpread) {
+ if (op == JSOP_EVAL) {
+ op = JSOP_SPREADEVAL;
+ } else if (op == JSOP_STRICTEVAL) {
+ op = JSOP_STRICTSPREADEVAL;
+ } else {
+ op = JSOP_SPREADCALL;
+ }
+ }
+
+ Node nextMember;
+ if (optionalKind == OptionalKind::Optional)
+ nextMember = handler.newOptionalCall(lhs, args);
+ else
+ nextMember = handler.newCall(lhs, args);
+
+ if (!nextMember)
+ return null();
+
+ handler.setOp(nextMember, op);
+ return nextMember;
+ }
+
+ Node args = handler.newArguments(pos());
+ if (!args) {
+ return null();
+ }
+
+ if (!taggedTemplate(yieldHandling, args, tt)) {
+ return null();
+ }
+
+ if (optionalKind == OptionalKind::Optional) {
+ error(JSMSG_BAD_OPTIONAL_TEMPLATE);
+ return null();
+ }
+
+ Node nextMember = handler.newTaggedTemplate(lhs, args);
+ if (!nextMember) {
+ return null();
+ }
+
+ handler.setOp(nextMember, op);
+ return nextMember;
+}
+
+template <class ParseHandler, typename CharT>
bool
Parser<ParseHandler, CharT>::checkLabelOrIdentifierReference(PropertyName* ident,
uint32_t offset,
diff -Nrup mozilla-OLD/js/src/frontend/Parser.h mozilla/js/src/frontend/Parser.h
--- mozilla-OLD/js/src/frontend/Parser.h 2022-01-10 07:07:24.310742706 +0300
+++ mozilla/js/src/frontend/Parser.h 2022-01-10 07:21:07.219924380 +0300
@@ -753,6 +753,10 @@ class Parser final
Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
PossibleError* possibleError = nullptr,
InvokedPrediction invoked = PredictUninvoked);
+ Node optionalExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+ TokenKind tt, bool allowCallSyntax = true,
+ PossibleError* possibleError = nullptr,
+ InvokedPrediction invoked = PredictUninvoked);
Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
TokenKind tt, bool allowCallSyntax = true,
PossibleError* possibleError = nullptr,
@@ -948,6 +952,18 @@ class Parser final
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
}
+ enum class OptionalKind {
+ NonOptional = 0,
+ Optional,
+ };
+ Node memberPropertyAccess(Node lhs, OptionalKind optionalKind = OptionalKind::NonOptional);
+ Node memberElemAccess(Node lhs, YieldHandling yieldHandling,
+ OptionalKind optionalKind = OptionalKind::NonOptional);
+ Node memberSuperCall(Node lhs, YieldHandling yieldHandling);
+ Node memberCall(TokenKind tt, Node lhs, YieldHandling yieldHandling,
+ PossibleError* possibleError,
+ OptionalKind optionalKind = OptionalKind::NonOptional);
+
static Node null() { return ParseHandler::null(); }
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
diff -Nrup mozilla-OLD/js/src/frontend/SyntaxParseHandler.h mozilla/js/src/frontend/SyntaxParseHandler.h
--- mozilla-OLD/js/src/frontend/SyntaxParseHandler.h 2022-01-10 07:07:24.310742706 +0300
+++ mozilla/js/src/frontend/SyntaxParseHandler.h 2022-01-10 07:21:07.219924380 +0300
@@ -59,6 +59,8 @@ class SyntaxParseHandler
// noticed).
NodeFunctionCall,
+ NodeOptionalFunctionCall,
+
// Node representing normal names which don't require any special
// casing.
NodeName,
@@ -72,7 +74,9 @@ class SyntaxParseHandler
NodePotentialAsyncKeyword,
NodeDottedProperty,
+ NodeOptionalDottedProperty,
NodeElement,
+ NodeOptionalElement,
// Destructuring target patterns can't be parenthesized: |([a]) = [3];|
// must be a syntax error. (We can't use NodeGeneric instead of these
@@ -243,6 +247,7 @@ class SyntaxParseHandler
Node newArguments(const TokenPos& pos) { return NodeGeneric; }
Node newCall(Node callee, Node args) { return NodeFunctionCall; }
+ Node newOptionalCall(Node callee, Node args) { return NodeOptionalFunctionCall; }
Node newSuperCall(Node callee, Node args) { return NodeGeneric; }
Node newTaggedTemplate(Node callee, Node args) { return NodeGeneric; }
@@ -265,6 +270,7 @@ class SyntaxParseHandler
Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
+ Node newOptionalChain(uint32_t begin, Node value) { return NodeGeneric; }
// Statements
@@ -327,8 +333,16 @@ class SyntaxParseHandler
return NodeDottedProperty;
}
+ Node newOptionalPropertyAccess(Node expr, Node key) {
+ return NodeOptionalDottedProperty;
+ }
+
Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
+ Node newOptionalPropertyByValue(Node pn, Node kid, uint32_t end) {
+ return NodeOptionalElement;
+ }
+
MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
return true;
}
diff -Nrup mozilla-OLD/js/src/frontend/TokenKind.h mozilla/js/src/frontend/TokenKind.h
--- mozilla-OLD/js/src/frontend/TokenKind.h 2022-01-10 07:07:24.311742699 +0300
+++ mozilla/js/src/frontend/TokenKind.h 2022-01-10 07:21:07.220924373 +0300
@@ -64,6 +64,7 @@
macro(DEC, "'--'") /* decrement */ \
macro(DOT, "'.'") /* member operator */ \
macro(TRIPLEDOT, "'...'") /* rest arguments and spread operator */ \
+ macro(OPTCHAIN, "'?.'") \
macro(LB, "'['") \
macro(RB, "']'") \
macro(LC, "'{'") \
diff -Nrup mozilla-OLD/js/src/frontend/TokenStream.cpp mozilla/js/src/frontend/TokenStream.cpp
--- mozilla-OLD/js/src/frontend/TokenStream.cpp 2022-01-10 07:07:24.312742692 +0300
+++ mozilla/js/src/frontend/TokenStream.cpp 2022-01-10 07:21:07.220924373 +0300
@@ -1853,7 +1853,23 @@ TokenStreamSpecific<CharT, AnyCharsAcces
goto out;
case '?':
- tp->type = matchChar('?') ? TOK_COALESCE : TOK_HOOK;
+ if (matchChar('.')) {
+ c = getCharIgnoreEOL();
+ if (JS7_ISDEC(c)) {
+ // if the code unit is followed by a number, for example it has the
+ // following form `<...> ?.5 <..> then it should be treated as a
+ // ternary rather than as an optional chain
+ tp->type = TOK_HOOK;
+ ungetCharIgnoreEOL(c);
+ ungetCharIgnoreEOL('.');
+ } else {
+ ungetCharIgnoreEOL(c);
+ tp->type = TOK_OPTCHAIN;
+ }
+ } else {
+ tp->type = matchChar('?') ? TOK_COALESCE : TOK_HOOK;
+ }
+
goto out;
case '!':
diff -Nrup mozilla-OLD/js/src/js.msg mozilla/js/src/js.msg
--- mozilla-OLD/js/src/js.msg 2022-01-10 07:07:24.312742692 +0300
+++ mozilla/js/src/js.msg 2022-01-10 07:21:07.221924366 +0300
@@ -358,6 +358,8 @@ MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0
MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration")
MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration")
MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed within functions")
+MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot be used with an optional chain")
+MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
// asm.js