mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-06 15:58:39 +09:00
Issue #1894 - Part 2: Implement support for nullish coalescing in the JS parser
Based on https://bugzilla.mozilla.org/show_bug.cgi?id=1566141
This commit is contained in:
parent
3efa23472c
commit
d7cdeaf313
8 changed files with 119 additions and 44 deletions
|
|
@ -326,6 +326,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_POSTINCREMENT:
|
||||
case PNK_PREDECREMENT:
|
||||
case PNK_POSTDECREMENT:
|
||||
case PNK_COALESCE:
|
||||
case PNK_OR:
|
||||
case PNK_AND:
|
||||
case PNK_BITOR:
|
||||
|
|
@ -752,15 +753,18 @@ FoldIncrementDecrement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHa
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldAndOr(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
|
||||
FoldLogical(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
|
||||
bool inGenexpLambda)
|
||||
{
|
||||
ParseNode* node = *nodePtr;
|
||||
|
||||
MOZ_ASSERT(node->isKind(PNK_AND) || node->isKind(PNK_OR));
|
||||
MOZ_ASSERT(node->isKind(PNK_AND) ||
|
||||
node->isKind(PNK_OR) ||
|
||||
node->isKind(PNK_COALESCE));
|
||||
MOZ_ASSERT(node->isArity(PN_LIST));
|
||||
|
||||
bool isOrNode = node->isKind(PNK_OR);
|
||||
bool isOrNode = (node->isKind(PNK_OR) ||
|
||||
node->isKind(PNK_COALESCE));
|
||||
ParseNode** elem = &node->pn_head;
|
||||
do {
|
||||
if (!Fold(cx, elem, parser, inGenexpLambda))
|
||||
|
|
@ -1738,9 +1742,10 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
return Fold(cx, &expr, parser, inGenexpLambda);
|
||||
return true;
|
||||
|
||||
case PNK_COALESCE:
|
||||
case PNK_AND:
|
||||
case PNK_OR:
|
||||
return FoldAndOr(cx, pnp, parser, inGenexpLambda);
|
||||
return FoldLogical(cx, pnp, parser, inGenexpLambda);
|
||||
|
||||
case PNK_FUNCTION:
|
||||
return FoldFunction(cx, pn, parser, inGenexpLambda);
|
||||
|
|
|
|||
|
|
@ -663,6 +663,7 @@ class NameResolver
|
|||
break;
|
||||
|
||||
// Nodes with arbitrary-expression children.
|
||||
case PNK_COALESCE:
|
||||
case PNK_OR:
|
||||
case PNK_AND:
|
||||
case PNK_BITOR:
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
}
|
||||
|
||||
// List nodes with all non-null children.
|
||||
case PNK_COALESCE:
|
||||
case PNK_OR:
|
||||
case PNK_AND:
|
||||
case PNK_BITOR:
|
||||
|
|
|
|||
|
|
@ -130,8 +130,12 @@ class ObjectBox;
|
|||
\
|
||||
/* \
|
||||
* Binary operators. \
|
||||
* These must be in the same order as TOK_OR and friends in TokenStream.h. \
|
||||
* These must be in the same order in several places: \
|
||||
* - the precedence table and JSOp code list in Parser.cpp \
|
||||
* - the binary operators in TokenKind.h \
|
||||
* - the first and last binary operator markers in ParseNode.h \
|
||||
*/ \
|
||||
F(COALESCE) \
|
||||
F(OR) \
|
||||
F(AND) \
|
||||
F(BITOR) \
|
||||
|
|
@ -189,7 +193,13 @@ enum ParseNodeKind
|
|||
FOR_EACH_PARSE_NODE_KIND(EMIT_ENUM)
|
||||
#undef EMIT_ENUM
|
||||
PNK_LIMIT, /* domain size */
|
||||
PNK_BINOP_FIRST = PNK_OR,
|
||||
/*
|
||||
* Binary operator markers.
|
||||
* These must be in the same order in several places:
|
||||
* - the precedence table and JSOp code list in Parser.cpp
|
||||
* - the binary operators in ParseNode.h and TokenKind.h
|
||||
*/
|
||||
PNK_BINOP_FIRST = PNK_COALESCE,
|
||||
PNK_BINOP_LAST = PNK_POW,
|
||||
PNK_ASSIGNMENT_START = PNK_ASSIGN,
|
||||
PNK_ASSIGNMENT_LAST = PNK_POWASSIGN
|
||||
|
|
@ -329,8 +339,9 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_POWASSIGN
|
||||
* PNK_CONDITIONAL ternary (cond ? trueExpr : falseExpr)
|
||||
* pn_kid1: cond, pn_kid2: then, pn_kid3: else
|
||||
* PNK_OR, list pn_head; list of pn_count subexpressions
|
||||
* PNK_AND, All of these operators are left-associative except (**).
|
||||
* PNK_COALESCE, list pn_head; list of pn_count subexpressions
|
||||
* PNK_OR, All of these operators are left-associative except (**).
|
||||
* PNK_AND
|
||||
* PNK_BITOR,
|
||||
* PNK_BITXOR,
|
||||
* PNK_BITAND,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "frontend/Parser.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h" // mozilla::ArrayLength
|
||||
#include "mozilla/Sprintf.h"
|
||||
|
||||
#include <new>
|
||||
|
|
@ -46,6 +47,7 @@
|
|||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
using mozilla::ArrayLength;
|
||||
using mozilla::Maybe;
|
||||
using mozilla::Move;
|
||||
using mozilla::Nothing;
|
||||
|
|
@ -7842,7 +7844,13 @@ Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
|
|||
return seq;
|
||||
}
|
||||
|
||||
/* These must be in the same order in several places:
|
||||
* - the precedence table in Parser.cpp
|
||||
* - the binary operators in ParseNode.h and TokenKind.h
|
||||
* - the first and last binary operator markers in ParseNode.h
|
||||
*/
|
||||
static const JSOp ParseNodeKindToJSOp[] = {
|
||||
JSOP_COALESCE,
|
||||
JSOP_OR,
|
||||
JSOP_AND,
|
||||
JSOP_BITOR,
|
||||
|
|
@ -7874,6 +7882,11 @@ BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
|
|||
{
|
||||
MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
|
||||
MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
|
||||
#ifdef DEBUG
|
||||
int jsopArraySize = ArrayLength(ParseNodeKindToJSOp);
|
||||
int parseNodeKindListSize = PNK_BINOP_LAST - PNK_BINOP_FIRST + 1;
|
||||
MOZ_ASSERT(jsopArraySize == parseNodeKindListSize);
|
||||
#endif
|
||||
return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
|
||||
}
|
||||
|
||||
|
|
@ -7884,34 +7897,39 @@ BinaryOpTokenKindToParseNodeKind(TokenKind tok)
|
|||
return ParseNodeKind(PNK_BINOP_FIRST + (tok - TOK_BINOP_FIRST));
|
||||
}
|
||||
|
||||
/* These must be in the same order in several places:
|
||||
* - the JSOp code list in Parser.cpp
|
||||
* - the binary operators in ParseNode.h and TokenKind.h
|
||||
*/
|
||||
static const int PrecedenceTable[] = {
|
||||
1, /* PNK_OR */
|
||||
2, /* PNK_AND */
|
||||
3, /* PNK_BITOR */
|
||||
4, /* PNK_BITXOR */
|
||||
5, /* PNK_BITAND */
|
||||
6, /* PNK_STRICTEQ */
|
||||
6, /* PNK_EQ */
|
||||
6, /* PNK_STRICTNE */
|
||||
6, /* PNK_NE */
|
||||
7, /* PNK_LT */
|
||||
7, /* PNK_LE */
|
||||
7, /* PNK_GT */
|
||||
7, /* PNK_GE */
|
||||
7, /* PNK_INSTANCEOF */
|
||||
7, /* PNK_IN */
|
||||
8, /* PNK_LSH */
|
||||
8, /* PNK_RSH */
|
||||
8, /* PNK_URSH */
|
||||
9, /* PNK_ADD */
|
||||
9, /* PNK_SUB */
|
||||
10, /* PNK_STAR */
|
||||
10, /* PNK_DIV */
|
||||
10, /* PNK_MOD */
|
||||
11 /* PNK_POW */
|
||||
1, /* PNK_COALESCE */
|
||||
2, /* PNK_OR */
|
||||
3, /* PNK_AND */
|
||||
4, /* PNK_BITOR */
|
||||
5, /* PNK_BITXOR */
|
||||
6, /* PNK_BITAND */
|
||||
7, /* PNK_STRICTEQ */
|
||||
7, /* PNK_EQ */
|
||||
7, /* PNK_STRICTNE */
|
||||
7, /* PNK_NE */
|
||||
8, /* PNK_LT */
|
||||
8, /* PNK_LE */
|
||||
8, /* PNK_GT */
|
||||
8, /* PNK_GE */
|
||||
8, /* PNK_INSTANCEOF */
|
||||
8, /* PNK_IN */
|
||||
9, /* PNK_LSH */
|
||||
9, /* PNK_RSH */
|
||||
9, /* PNK_URSH */
|
||||
10, /* PNK_ADD */
|
||||
10, /* PNK_SUB */
|
||||
11, /* PNK_STAR */
|
||||
11, /* PNK_DIV */
|
||||
11, /* PNK_MOD */
|
||||
12 /* PNK_POW */
|
||||
};
|
||||
|
||||
static const int PRECEDENCE_CLASSES = 11;
|
||||
static const int PRECEDENCE_CLASSES = 12;
|
||||
|
||||
static int
|
||||
Precedence(ParseNodeKind pnk) {
|
||||
|
|
@ -7926,6 +7944,8 @@ Precedence(ParseNodeKind pnk) {
|
|||
return PrecedenceTable[pnk - PNK_BINOP_FIRST];
|
||||
}
|
||||
|
||||
enum class EnforcedParentheses : uint8_t { CoalesceExpr, AndOrExpr, None };
|
||||
|
||||
template <typename ParseHandler>
|
||||
MOZ_ALWAYS_INLINE typename ParseHandler::Node
|
||||
Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
|
||||
|
|
@ -7942,6 +7962,7 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling
|
|||
ParseNodeKind kindStack[PRECEDENCE_CLASSES];
|
||||
int depth = 0;
|
||||
Node pn;
|
||||
EnforcedParentheses unparenthesizedExpression = EnforcedParentheses::None;
|
||||
for (;;) {
|
||||
pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
|
||||
if (!pn)
|
||||
|
|
@ -7959,11 +7980,42 @@ Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling
|
|||
// pending expression error now.
|
||||
if (possibleError && !possibleError->checkForExpressionError())
|
||||
return null();
|
||||
// Report an error for unary expressions on the LHS of **.
|
||||
if (tok == TOK_POW && handler.isUnparenthesizedUnaryExpression(pn)) {
|
||||
error(JSMSG_BAD_POW_LEFTSIDE);
|
||||
return null();
|
||||
|
||||
switch (tok) {
|
||||
// Report an error for unary expressions on the LHS of **.
|
||||
case TOK_POW:
|
||||
if (handler.isUnparenthesizedUnaryExpression(pn)) {
|
||||
error(JSMSG_BAD_POW_LEFTSIDE);
|
||||
return null();
|
||||
}
|
||||
break;
|
||||
case TOK_OR:
|
||||
case TOK_AND:
|
||||
// Report an error if ?? is on the LHS of the expression.
|
||||
// Mixing other logical operators with the nullish coalescing
|
||||
// operator is disallowed unless one expression is parenthesized.
|
||||
if (unparenthesizedExpression == EnforcedParentheses::CoalesceExpr) {
|
||||
error(JSMSG_BAD_COALESCE_MIXING);
|
||||
return null();
|
||||
}
|
||||
// If we have not detected a mixing error at this point, record that
|
||||
// we have an unparenthesized expression, in case we have one later.
|
||||
unparenthesizedExpression = EnforcedParentheses::AndOrExpr;
|
||||
break;
|
||||
case TOK_COALESCE:
|
||||
if (unparenthesizedExpression == EnforcedParentheses::AndOrExpr) {
|
||||
error(JSMSG_BAD_COALESCE_MIXING);
|
||||
return null();
|
||||
}
|
||||
// If we have not detected a mixing error at this point, record that
|
||||
// we have an unparenthesized expression, in case we have one later.
|
||||
unparenthesizedExpression = EnforcedParentheses::CoalesceExpr;
|
||||
break;
|
||||
default:
|
||||
// Do nothing in other cases.
|
||||
break;
|
||||
}
|
||||
|
||||
pnk = BinaryOpTokenKindToParseNodeKind(tok);
|
||||
} else {
|
||||
tok = TOK_EOF;
|
||||
|
|
|
|||
|
|
@ -152,11 +152,15 @@
|
|||
* range-testing. \
|
||||
*/ \
|
||||
/* \
|
||||
* Binary operators tokens, TOK_OR thru TOK_POW. These must be in the same \
|
||||
* order as F(OR) and friends in FOR_EACH_PARSE_NODE_KIND in ParseNode.h. \
|
||||
* Binary operators tokens. \
|
||||
* These must be in the same order in several places: \
|
||||
* - the precedence table and JSOp code list in Parser.cpp \
|
||||
* - the binary operators in ParseNode.h \
|
||||
* - the first and last binary operator markers in ParseNode.h \
|
||||
*/ \
|
||||
macro(COALESCE, "'\?\?'") /* escapes to avoid trigraphs warning */ \
|
||||
range(BINOP_FIRST, COALESCE) \
|
||||
macro(OR, "'||'") /* logical or */ \
|
||||
range(BINOP_FIRST, OR) \
|
||||
macro(AND, "'&&'") /* logical and */ \
|
||||
macro(BITOR, "'|'") /* bitwise-or */ \
|
||||
macro(BITXOR, "'^'") /* bitwise-xor */ \
|
||||
|
|
@ -196,7 +200,7 @@
|
|||
macro(DIV, "'/'") \
|
||||
macro(MOD, "'%'") \
|
||||
macro(POW, "'**'") \
|
||||
range(BINOP_LAST, POW) \
|
||||
range(BINOP_LAST, POW) \
|
||||
\
|
||||
/* Unary operation tokens. */ \
|
||||
macro(TYPEOF, "keyword 'typeof'") \
|
||||
|
|
|
|||
|
|
@ -1260,8 +1260,8 @@ enum FirstCharKind {
|
|||
LastCharKind = Other
|
||||
};
|
||||
|
||||
// OneChar: 40, 41, 44, 58, 59, 63, 91, 93, 123, 125, 126:
|
||||
// '(', ')', ',', ':', ';', '?', '[', ']', '{', '}', '~'
|
||||
// OneChar: 40, 41, 44, 58, 59, 91, 93, 123, 125, 126:
|
||||
// '(', ')', ',', ':', ';', '[', ']', '{', '}', '~'
|
||||
// Ident: 36, 65..90, 95, 97..122: '$', 'A'..'Z', '_', 'a'..'z'
|
||||
// Dot: 46: '.'
|
||||
// Equals: 61: '='
|
||||
|
|
@ -1811,7 +1811,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
tp->type = TOK_OPTCHAIN;
|
||||
}
|
||||
} else {
|
||||
tp->type = TOK_HOOK;
|
||||
tp->type = matchChar('?') ? TOK_COALESCE : TOK_HOOK;
|
||||
}
|
||||
goto out;
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used
|
|||
MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions")
|
||||
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
|
||||
MSG_DEF(JSMSG_BAD_BINDING, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
|
||||
MSG_DEF(JSMSG_BAD_COALESCE_MIXING, 0, JSEXN_SYNTAXERR, "cannot use `??` unparenthesized within `||` and `&&` expressions")
|
||||
MSG_DEF(JSMSG_BAD_CONST_DECL, 0, JSEXN_SYNTAXERR, "missing = in const declaration")
|
||||
MSG_DEF(JSMSG_BAD_CONTINUE, 0, JSEXN_SYNTAXERR, "continue must be inside loop")
|
||||
MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue