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.
*/
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<CaseClause>() : 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<SwitchStatement>()))
return false;
break;

View file

@ -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 {

View file

@ -661,9 +661,11 @@ class FullParseHandler
return new_<TernaryNode>(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_<BinaryNode>(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList);
ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant,
ParseNode* lexicalForCaseList, bool hasDefault)
{
return new_<SwitchStatement>(begin, discriminant, lexicalForCaseList, hasDefault);
}
ParseNode* newCaseOrDefault(uint32_t begin, ParseNode* expr, ParseNode* body) {

View file

@ -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<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 {
ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos)
: BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding)

View file

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