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