From 36b5a56242f0c52f55804631a0422a49bf189b32 Mon Sep 17 00:00:00 2001 From: Martok Date: Fri, 24 Mar 2023 11:06:37 +0100 Subject: [PATCH] Issue #2173 - Store the info about the existence of the default case into the switch ParseNode Based-on: m-c 1477157 --- js/src/frontend/BytecodeEmitter.cpp | 32 ++++++++----------- js/src/frontend/BytecodeEmitter.h | 2 +- js/src/frontend/FullParseHandler.h | 8 +++-- js/src/frontend/ParseNode.h | 46 +++++++++++++++++++++++++++- js/src/frontend/Parser.cpp | 2 +- js/src/frontend/SyntaxParseHandler.h | 5 ++- 6 files changed, 68 insertions(+), 27 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 3997d1cf3e..f2acb2942f 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2047,23 +2047,23 @@ BytecodeEmitter::emitNumberOp(double dval) * into emitTree which is recursive and uses relatively little stack space. */ MOZ_NEVER_INLINE bool -BytecodeEmitter::emitSwitch(ParseNode* pn) +BytecodeEmitter::emitSwitch(SwitchStatement* pn) { - ParseNode* lexical = pn->pn_right; - MOZ_ASSERT(lexical->isKind(PNK_LEXICALSCOPE)); - ParseNode* cases = lexical->scopeBody(); + ParseNode& lexical = pn->lexicalForCaseList(); + MOZ_ASSERT(lexical.isKind(PNK_LEXICALSCOPE)); + ParseNode* cases = lexical.scopeBody(); MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); SwitchEmitter se(this); if (!se.emitDiscriminant(Some(pn->pn_pos.begin))) return false; - if (!emitTree(pn->pn_left)) + if (!emitTree(&pn->discriminant())) return false; // Enter the scope before pushing the switch BreakableControl since all // breaks are under this scope. - if (!lexical->isEmptyScope()) { - if (!se.emitLexical(lexical->scopeBindings())) + if (!lexical.isEmptyScope()) { + if (!se.emitLexical(lexical.scopeBindings())) return false; // A switch statement may contain hoisted functions inside its @@ -2082,34 +2082,26 @@ BytecodeEmitter::emitSwitch(ParseNode* pn) } SwitchEmitter::TableGenerator tableGen(this); - uint32_t caseCount = cases->pn_count; + uint32_t caseCount = cases->pn_count - (pn->hasDefault() ? 1 : 0); CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as() : nullptr; if (caseCount == 0) { tableGen.finish(0); - } else if (caseCount == 1 && firstCase->isDefault()) { - caseCount = 0; - tableGen.finish(0); } else { for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) { - if (caseNode->isDefault()) { - caseCount--; // one of the "cases" was the default - continue; - } - - if (tableGen.isInvalid()) + if (caseNode->isDefault()) continue; ParseNode* caseValue = caseNode->caseExpression(); if (caseValue->getKind() != PNK_NUMBER) { tableGen.setInvalid(); - continue; + break; } int32_t i; if (!NumberIsInt32(caseValue->pn_dval, &i)) { tableGen.setInvalid(); - continue; + break; } if (!tableGen.addNumber(i)) @@ -8664,7 +8656,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_SWITCH: - if (!emitSwitch(pn)) + if (!emitSwitch(&pn->as())) return false; break; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 70af327f38..1a2c87580a 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -591,7 +591,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn); MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body, EmitLineNumberNote emitLineNote = EMIT_LINENOTE); - MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(ParseNode* pn); + MOZ_NEVER_INLINE MOZ_MUST_USE bool emitSwitch(SwitchStatement* pn); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(ParseNode* pn); enum DestructuringFlavor { diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index d5a8c1ab4e..87b4350308 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -661,9 +661,11 @@ class FullParseHandler return new_(kind, JSOP_NOP, target, nullptr, iteratedExpr, pos); } - ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* caseList) { - TokenPos pos(begin, caseList->pn_pos.end); - return new_(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList); + ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, + ParseNode* lexicalForCaseList, bool hasDefault) + { + return new_(begin, discriminant, lexicalForCaseList, hasDefault); + } ParseNode* newCaseOrDefault(uint32_t begin, ParseNode* expr, ParseNode* body) { diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 7f42b57eeb..51d43a36a1 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -258,6 +258,7 @@ IsTypeofKind(ParseNodeKind kind) * PNK_SWITCH binary pn_left: discriminant * pn_right: PNK_LEXICALSCOPE node that contains the list * of PNK_CASE nodes, with at most one default node. + * hasDefault: true if there's a default case * PNK_CASE binary pn_left: case-expression if CaseClause, or * null if DefaultClause * pn_right: PNK_STATEMENTLIST node for this case's @@ -589,6 +590,7 @@ class ParseNode union { unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */ bool isStatic; /* only for PNK_CLASSMETHOD */ + bool hasDefault; /* only for PNK_SWITCH */ }; } binary; struct { /* one kid if unary */ @@ -1375,7 +1377,7 @@ struct CallSiteNode : public ListNode { struct ClassMethod : public BinaryNode { /* - * Method defintions often keep a name and function body that overlap, + * Method definitions often keep a name and function body that overlap, * so explicitly define the beginning and end here. */ ClassMethod(ParseNode* name, ParseNode* body, JSOp op, bool isStatic) @@ -1401,6 +1403,48 @@ struct ClassMethod : public BinaryNode { } }; +struct SwitchStatement : public BinaryNode { + SwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* lexicalForCaseList, + bool hasDefault) + : BinaryNode(PNK_SWITCH, JSOP_NOP, + TokenPos(begin, lexicalForCaseList->pn_pos.end), + discriminant, lexicalForCaseList) + { +#ifdef DEBUG + MOZ_ASSERT(lexicalForCaseList->isKind(PNK_LEXICALSCOPE)); + ParseNode* cases = lexicalForCaseList->scopeBody(); + MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); + bool found = false; + CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as() : nullptr; + for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) { + if (caseNode->isDefault()) { + found = true; + break; + } + } + MOZ_ASSERT(found == hasDefault); +#endif + + pn_u.binary.hasDefault = hasDefault; + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(PNK_SWITCH); + MOZ_ASSERT_IF(match, node.isArity(PN_BINARY)); + return match; + } + + ParseNode& discriminant() const { + return *pn_u.binary.left; + } + ParseNode& lexicalForCaseList() const { + return *pn_u.binary.right; + } + bool hasDefault() const { + return pn_u.binary.hasDefault; + } +}; + struct ClassNames : public BinaryNode { ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos) : BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 779cd692c6..3207e2b9fc 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6506,7 +6506,7 @@ Parser::switchStatement(YieldHandling yieldHandling) handler.setEndPosition(caseList, pos().end); - return handler.newSwitchStatement(begin, discriminant, caseList); + return handler.newSwitchStatement(begin, discriminant, caseList, seenDefault); } template diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index bca7400e55..c2abffbd06 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -343,7 +343,10 @@ class SyntaxParseHandler Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; } Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; } Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; } - Node newSwitchStatement(uint32_t begin, Node discriminant, Node caseList) { return NodeGeneric; } + Node newSwitchStatement(uint32_t begin, Node discriminant, Node lexicalForCaseList, bool hasDefault) + { + return NodeGeneric; + } Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; } Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; } Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }