Issue #2173 - Store the info about the existence of the default case into the switch ParseNode

Based-on: m-c 1477157
This commit is contained in:
Martok 2023-03-24 11:06:37 +01:00 • committed by roytam1
commit 36b5a56242
6 changed files with 68 additions and 27 deletions

View file

@ -2047,23 +2047,23 @@ BytecodeEmitter::emitNumberOp(double dval)
* into emitTree which is recursive and uses relatively little stack space. * into emitTree which is recursive and uses relatively little stack space.
*/ */
MOZ_NEVER_INLINE bool MOZ_NEVER_INLINE bool
BytecodeEmitter::emitSwitch(ParseNode* pn) BytecodeEmitter::emitSwitch(SwitchStatement* pn)
{ {
ParseNode* lexical = pn->pn_right; ParseNode& lexical = pn->lexicalForCaseList();
MOZ_ASSERT(lexical->isKind(PNK_LEXICALSCOPE)); MOZ_ASSERT(lexical.isKind(PNK_LEXICALSCOPE));
ParseNode* cases = lexical->scopeBody(); ParseNode* cases = lexical.scopeBody();
MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST));
SwitchEmitter se(this); SwitchEmitter se(this);
if (!se.emitDiscriminant(Some(pn->pn_pos.begin))) if (!se.emitDiscriminant(Some(pn->pn_pos.begin)))
return false; return false;
if (!emitTree(pn->pn_left)) if (!emitTree(&pn->discriminant()))
return false; return false;
// Enter the scope before pushing the switch BreakableControl since all // Enter the scope before pushing the switch BreakableControl since all
// breaks are under this scope. // breaks are under this scope.
if (!lexical->isEmptyScope()) { if (!lexical.isEmptyScope()) {
if (!se.emitLexical(lexical->scopeBindings())) if (!se.emitLexical(lexical.scopeBindings()))
return false; return false;
// A switch statement may contain hoisted functions inside its // A switch statement may contain hoisted functions inside its
@ -2082,34 +2082,26 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
} }
SwitchEmitter::TableGenerator tableGen(this); 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<CaseClause>() : nullptr; CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as<CaseClause>() : nullptr;
if (caseCount == 0) { if (caseCount == 0) {
tableGen.finish(0); tableGen.finish(0);
} else if (caseCount == 1 && firstCase->isDefault()) {
caseCount = 0;
tableGen.finish(0);
} else { } else {
for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) { for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
if (caseNode->isDefault()) { if (caseNode->isDefault())
caseCount--; // one of the "cases" was the default
continue;
}
if (tableGen.isInvalid())
continue; continue;
ParseNode* caseValue = caseNode->caseExpression(); ParseNode* caseValue = caseNode->caseExpression();
if (caseValue->getKind() != PNK_NUMBER) { if (caseValue->getKind() != PNK_NUMBER) {
tableGen.setInvalid(); tableGen.setInvalid();
continue; break;
} }
int32_t i; int32_t i;
if (!NumberIsInt32(caseValue->pn_dval, &i)) { if (!NumberIsInt32(caseValue->pn_dval, &i)) {
tableGen.setInvalid(); tableGen.setInvalid();
continue; break;
} }
if (!tableGen.addNumber(i)) if (!tableGen.addNumber(i))
@ -8664,7 +8656,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
break; break;
case PNK_SWITCH: case PNK_SWITCH:
if (!emitSwitch(pn)) if (!emitSwitch(&pn->as<SwitchStatement>()))
return false; return false;
break; break;

View file

@ -591,7 +591,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(ParseNode* pn);
MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body, MOZ_MUST_USE bool emitLexicalScopeBody(ParseNode* body,
EmitLineNumberNote emitLineNote = EMIT_LINENOTE); 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); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitTry(ParseNode* pn);
enum DestructuringFlavor { enum DestructuringFlavor {

View file

@ -661,9 +661,11 @@ class FullParseHandler
return new_<TernaryNode>(kind, JSOP_NOP, target, nullptr, iteratedExpr, pos); return new_<TernaryNode>(kind, JSOP_NOP, target, nullptr, iteratedExpr, pos);
} }
ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* caseList) { ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant,
TokenPos pos(begin, caseList->pn_pos.end); ParseNode* lexicalForCaseList, bool hasDefault)
return new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList); {
return new_<SwitchStatement>(begin, discriminant, lexicalForCaseList, hasDefault);
} }
ParseNode* newCaseOrDefault(uint32_t begin, ParseNode* expr, ParseNode* body) { ParseNode* newCaseOrDefault(uint32_t begin, ParseNode* expr, ParseNode* body) {

View file

@ -258,6 +258,7 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_SWITCH binary pn_left: discriminant * PNK_SWITCH binary pn_left: discriminant
* pn_right: PNK_LEXICALSCOPE node that contains the list * pn_right: PNK_LEXICALSCOPE node that contains the list
* of PNK_CASE nodes, with at most one default node. * 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 * PNK_CASE binary pn_left: case-expression if CaseClause, or
* null if DefaultClause * null if DefaultClause
* pn_right: PNK_STATEMENTLIST node for this case's * pn_right: PNK_STATEMENTLIST node for this case's
@ -589,6 +590,7 @@ class ParseNode
union { union {
unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */ unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */
bool isStatic; /* only for PNK_CLASSMETHOD */ bool isStatic; /* only for PNK_CLASSMETHOD */
bool hasDefault; /* only for PNK_SWITCH */
}; };
} binary; } binary;
struct { /* one kid if unary */ struct { /* one kid if unary */
@ -1375,7 +1377,7 @@ struct CallSiteNode : public ListNode {
struct ClassMethod : public BinaryNode { 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. * so explicitly define the beginning and end here.
*/ */
ClassMethod(ParseNode* name, ParseNode* body, JSOp op, bool isStatic) 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<CaseClause>() : 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 { struct ClassNames : public BinaryNode {
ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos) ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos)
: BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding) : BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding)

View file

@ -6506,7 +6506,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
handler.setEndPosition(caseList, pos().end); handler.setEndPosition(caseList, pos().end);
return handler.newSwitchStatement(begin, discriminant, caseList); return handler.newSwitchStatement(begin, discriminant, caseList, seenDefault);
} }
template <typename ParseHandler> template <typename ParseHandler>

View file

@ -343,7 +343,10 @@ class SyntaxParseHandler
Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; } 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 newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
Node newWhileStatement(uint32_t begin, Node cond, Node body) { 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 newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; } Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; } Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }