diff options
Diffstat (limited to 'js/src/frontend')
-rw-r--r-- | js/src/frontend/BytecodeEmitter.cpp | 2 | ||||
-rw-r--r-- | js/src/frontend/FoldConstants.cpp | 46 | ||||
-rw-r--r-- | js/src/frontend/NameFunctions.cpp | 1 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.cpp | 1 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.h | 19 | ||||
-rw-r--r-- | js/src/frontend/Parser.cpp | 110 | ||||
-rw-r--r-- | js/src/frontend/TokenKind.h | 12 | ||||
-rw-r--r-- | js/src/frontend/TokenStream.cpp | 6 |
8 files changed, 141 insertions, 56 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index b5a96f32d9..e21ab72407 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3334,6 +3334,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_CATCHLIST: // Strict equality operations and logical operators are well-behaved and // perform no conversions. + case PNK_COALESCE: case PNK_OR: case PNK_AND: case PNK_STRICTEQ: @@ -11355,6 +11356,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: return false; break; + case PNK_COALESCE: case PNK_OR: case PNK_AND: if (!emitLogical(pn)) diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 979af29b42..c3354e44a4 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -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: @@ -482,15 +483,19 @@ IsEffectless(ParseNode* node) enum Truthiness { Truthy, Falsy, Unknown }; static Truthiness -Boolish(ParseNode* pn) +Boolish(ParseNode* pn, bool isNullish = false) { switch (pn->getKind()) { - case PNK_NUMBER: - return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy; + case PNK_NUMBER: { + bool isNonZeroNumber = (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)); + return (isNullish || isNonZeroNumber) ? Truthy : Falsy; + } case PNK_STRING: - case PNK_TEMPLATE_STRING: - return (pn->pn_atom->length() > 0) ? Truthy : Falsy; + case PNK_TEMPLATE_STRING: { + bool isNonZeroLengthString = (pn->pn_atom->length() > 0); + return (isNullish || isNonZeroLengthString) ? Truthy : Falsy; + } case PNK_TRUE: case PNK_FUNCTION: @@ -498,6 +503,8 @@ Boolish(ParseNode* pn) return Truthy; case PNK_FALSE: + return isNullish ? Truthy : Falsy; + case PNK_NULL: case PNK_RAW_UNDEFINED: return Falsy; @@ -752,21 +759,24 @@ 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)); + bool isCoalesceNode = node->isKind(PNK_COALESCE); + bool isOrNode = node->isKind(PNK_OR); + bool isAndNode = node->isKind(PNK_AND); + + MOZ_ASSERT(isCoalesceNode || isOrNode || isAndNode); MOZ_ASSERT(node->isArity(PN_LIST)); - bool isOrNode = node->isKind(PNK_OR); ParseNode** elem = &node->pn_head; do { if (!Fold(cx, elem, parser, inGenexpLambda)) return false; - Truthiness t = Boolish(*elem); + Truthiness t = Boolish(*elem, isCoalesceNode); // If we don't know the constant-folded node's truthiness, we can't // reduce this node with its surroundings. Continue folding any @@ -776,11 +786,16 @@ FoldAndOr(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& p continue; } + bool terminateEarly = (isOrNode && t == Truthy) || + (isAndNode && t == Falsy) || + (isCoalesceNode && t == Truthy); + // If the constant-folded node's truthiness will terminate the - // condition -- `a || true || expr` or |b && false && expr| -- then - // trailing nodes will never be evaluated. Truncate the list after - // the known-truthiness node, as it's the overall result. - if ((t == Truthy) == isOrNode) { + // condition -- `a || true || expr` or `b && false && expr` or + // `false ?? c ?? expr` -- then trailing nodes will never be + // evaluated. Truncate the list after the known-truthiness node, + // as it's the overall result. + if (terminateEarly) { ParseNode* afterNext; for (ParseNode* next = (*elem)->pn_next; next; next = afterNext) { afterNext = next->pn_next; @@ -795,8 +810,6 @@ FoldAndOr(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& p break; } - MOZ_ASSERT((t == Truthy) == !isOrNode); - // We've encountered a vacuous node that'll never short- circuit // evaluation. if ((*elem)->pn_next) { @@ -1738,9 +1751,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); diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index a36427d835..ebf83fb235 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -663,6 +663,7 @@ class NameResolver break; // Nodes with arbitrary-expression children. + case PNK_COALESCE: case PNK_OR: case PNK_AND: case PNK_BITOR: diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index dfbd724e2e..0b6278c7eb 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -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: diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 0ad0fe0885..377eb999d9 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -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, diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 5202b71545..a66183b4a8 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -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; diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h index 27da8ecf3e..f11ceda33e 100644 --- a/js/src/frontend/TokenKind.h +++ b/js/src/frontend/TokenKind.h @@ -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'") \ diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 8f9e206d9f..b464b23048 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -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; |