mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-05 03:54:35 +00:00
Bug 1172895 - Handle chains of if/else statements without overrecursing in the parser and bytecode emitter, r=jorendorff.
This commit is contained in:
parent
4f232ef5f5
commit
17eb84f5a3
@ -1904,6 +1904,8 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
restart:
|
||||
|
||||
switch (pn->getKind()) {
|
||||
// Trivial cases with no side effects.
|
||||
case PNK_NEWTARGET:
|
||||
@ -2151,6 +2153,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_IF:
|
||||
case PNK_CONDITIONAL:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
@ -2161,22 +2164,8 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
return checkSideEffects(pn->pn_kid3, answer);
|
||||
|
||||
case PNK_IF:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (!checkSideEffects(pn->pn_kid2, answer))
|
||||
return false;
|
||||
if (*answer)
|
||||
return true;
|
||||
if (ParseNode* elseNode = pn->pn_kid3) {
|
||||
if (!checkSideEffects(elseNode, answer))
|
||||
return false;
|
||||
}
|
||||
if ((pn = pn->pn_kid3))
|
||||
goto restart;
|
||||
return true;
|
||||
|
||||
// Function calls can invoke non-local code.
|
||||
|
@ -58,6 +58,8 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
restart:
|
||||
|
||||
// With a better-typed AST, we would have distinct parse node classes for
|
||||
// expressions and for statements and would characterize expressions with
|
||||
// ExpressionKind and statements with StatementKind. Perhaps someday. In
|
||||
@ -164,8 +166,8 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
if (*result)
|
||||
return true;
|
||||
|
||||
if (ParseNode* alternative = node->pn_kid3)
|
||||
return ContainsHoistedDeclaration(cx, alternative, result);
|
||||
if ((node = node->pn_kid3))
|
||||
goto restart;
|
||||
|
||||
*result = false;
|
||||
return true;
|
||||
@ -606,13 +608,27 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
FullParseHandler& handler, const ReadOnlyCompileOptions& options,
|
||||
bool inGenexpLambda, SyntacticContext sc)
|
||||
{
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
ParseNode** restartNode = nullptr;
|
||||
SyntacticContext restartContext;
|
||||
|
||||
bool mightHaveHoistedDeclarations = true;
|
||||
|
||||
if (false) {
|
||||
restart:
|
||||
if (!restartNode)
|
||||
return true;
|
||||
pnp = restartNode;
|
||||
sc = restartContext;
|
||||
restartNode = nullptr;
|
||||
}
|
||||
|
||||
ParseNode* pn = *pnp;
|
||||
ParseNode* pn1 = nullptr;
|
||||
ParseNode* pn2 = nullptr;
|
||||
ParseNode* pn3 = nullptr;
|
||||
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
// First, recursively fold constants on the children of this node.
|
||||
switch (pn->getArity()) {
|
||||
case PN_CODE:
|
||||
@ -673,8 +689,13 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
pn2 = pn->pn_kid2;
|
||||
|
||||
if (pn->pn_kid3) {
|
||||
if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other))
|
||||
return false;
|
||||
if (pn->isKind(PNK_IF) || pn->isKind(PNK_CONDITIONAL)) {
|
||||
restartNode = &pn->pn_kid3;
|
||||
restartContext = SyntacticContext::Other;
|
||||
} else {
|
||||
if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pn3 = pn->pn_kid3;
|
||||
break;
|
||||
@ -748,11 +769,11 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
// pn is the immediate child in question. Its descendants were already
|
||||
// constant-folded above, so we're done.
|
||||
if (sc == SyntacticContext::Delete)
|
||||
return true;
|
||||
goto restart;
|
||||
|
||||
switch (pn->getKind()) {
|
||||
case PNK_IF:
|
||||
{
|
||||
if (mightHaveHoistedDeclarations) {
|
||||
bool result;
|
||||
if (ParseNode* consequent = pn2) {
|
||||
if (!ContainsHoistedDeclaration(cx, consequent, &result))
|
||||
@ -767,6 +788,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
break;
|
||||
}
|
||||
}
|
||||
mightHaveHoistedDeclarations = false;
|
||||
/* FALL THROUGH */
|
||||
|
||||
case PNK_CONDITIONAL:
|
||||
@ -788,7 +810,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
break;
|
||||
default:
|
||||
/* Early return to dodge common code that copies pn2 to pn. */
|
||||
return true;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
@ -798,6 +820,8 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
#endif
|
||||
|
||||
if (pn2 && !pn2->isDefn()) {
|
||||
if (restartNode && *restartNode == pn2)
|
||||
restartNode = pnp;
|
||||
ReplaceNode(pnp, pn2);
|
||||
pn = pn2;
|
||||
}
|
||||
@ -814,8 +838,11 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
pn->setArity(PN_LIST);
|
||||
pn->makeEmpty();
|
||||
}
|
||||
if (pn3 && pn3 != pn2)
|
||||
if (pn3 && pn3 != pn2) {
|
||||
if (restartNode && *restartNode == pn3)
|
||||
restartNode = nullptr;
|
||||
handler.freeTree(pn3);
|
||||
}
|
||||
break;
|
||||
|
||||
case PNK_OR:
|
||||
@ -1028,7 +1055,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
|
||||
default:
|
||||
/* Return early to dodge the common PNK_NUMBER code. */
|
||||
return true;
|
||||
goto restart;
|
||||
}
|
||||
pn->setKind(PNK_NUMBER);
|
||||
pn->setOp(JSOP_DOUBLE);
|
||||
@ -1131,7 +1158,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -4631,42 +4631,62 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::ifStatement(YieldHandling yieldHandling)
|
||||
{
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
/* An IF node has three kids: condition, then, and optional else. */
|
||||
Node cond = condition(InAllowed, yieldHandling);
|
||||
if (!cond)
|
||||
return null();
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
if (tt == TOK_SEMI) {
|
||||
if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
|
||||
return null();
|
||||
}
|
||||
Vector<Node, 4> condList(context), thenList(context);
|
||||
Vector<uint32_t, 4> posList(context);
|
||||
Node elseBranch;
|
||||
|
||||
StmtInfoPC stmtInfo(context);
|
||||
PushStatementPC(pc, &stmtInfo, STMT_IF);
|
||||
Node thenBranch = statement(yieldHandling);
|
||||
if (!thenBranch)
|
||||
return null();
|
||||
|
||||
Node elseBranch;
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
|
||||
return null();
|
||||
if (matched) {
|
||||
stmtInfo.type = STMT_ELSE;
|
||||
elseBranch = statement(yieldHandling);
|
||||
if (!elseBranch)
|
||||
while (true) {
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
/* An IF node has three kids: condition, then, and optional else. */
|
||||
Node cond = condition(InAllowed, yieldHandling);
|
||||
if (!cond)
|
||||
return null();
|
||||
} else {
|
||||
elseBranch = null();
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.peekToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
if (tt == TOK_SEMI) {
|
||||
if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
|
||||
return null();
|
||||
}
|
||||
|
||||
Node thenBranch = statement(yieldHandling);
|
||||
if (!thenBranch)
|
||||
return null();
|
||||
|
||||
if (!condList.append(cond) || !thenList.append(thenBranch) || !posList.append(begin))
|
||||
return null();
|
||||
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
|
||||
return null();
|
||||
if (matched) {
|
||||
if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
|
||||
return null();
|
||||
if (matched)
|
||||
continue;
|
||||
elseBranch = statement(yieldHandling);
|
||||
if (!elseBranch)
|
||||
return null();
|
||||
} else {
|
||||
elseBranch = null();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
PopStatementPC(tokenStream, pc);
|
||||
return handler.newIfStatement(begin, cond, thenBranch, elseBranch);
|
||||
|
||||
for (int i = condList.length() - 1; i >= 0; i--) {
|
||||
elseBranch = handler.newIfStatement(posList[i], condList[i], thenList[i], elseBranch);
|
||||
if (!elseBranch)
|
||||
return null();
|
||||
}
|
||||
|
||||
return elseBranch;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
|
Loading…
Reference in New Issue
Block a user