Bug 1172895 - Handle chains of if/else statements without overrecursing in the parser and bytecode emitter, r=jorendorff.

This commit is contained in:
Brian Hackett 2015-07-17 09:26:51 -07:00
parent 4f232ef5f5
commit 17eb84f5a3
3 changed files with 92 additions and 56 deletions

View File

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

View File

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

View File

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