Avoid stack overflow on super-large expressions of binary operators.

A small change to optimize very simple binary operators in the parser. This avoids building very large parse trees for super-large expressions and folds binary operators involving two scalars directly in the parser. I've limited this to simple scalars since it's easy to prove they don't have anything too interesting going on in the other analysis phases leading up to BinaryOpExpression's normal folding. This works for all binary operators.
Esse commit está contido em:
mikemag
2013-04-02 11:18:57 -07:00
commit de Sara Golemon
commit ab9e26f8af
8 arquivos alterados com 82 adições e 6 exclusões
@@ -422,13 +422,21 @@ static ExpressionPtr makeIsNull(AnalysisResultConstPtr ar,
return result;
}
// foldConst() is callable from the parse phase as well as the analysis phase.
// We take advantage of this during the parse phase to reduce very simple
// expressions down to a single scalar and keep the parse tree smaller,
// especially in cases of long chains of binary operators. However, we limit
// the effectivness of this during parse to ensure that we eliminate only
// very simple scalars that don't require analysis in later phases. For now,
// that's just simply scalar values.
ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
ExpressionPtr optExp;
Variant v1;
Variant v2;
if (!m_exp2->getScalarValue(v2)) {
if (m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
if ((ar->getPhase() != AnalysisResult::ParseAllFiles) &&
m_exp1->isScalar() && m_exp1->getScalarValue(v1)) {
switch (m_op) {
case T_IS_IDENTICAL:
case T_IS_NOT_IDENTICAL:
@@ -486,15 +494,23 @@ ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
if (m_exp1->isScalar()) {
if (!m_exp1->getScalarValue(v1)) return ExpressionPtr();
try {
ScalarExpressionPtr scalar1 =
dynamic_pointer_cast<ScalarExpression>(m_exp1);
ScalarExpressionPtr scalar2 =
dynamic_pointer_cast<ScalarExpression>(m_exp2);
// Some data, like the values of __CLASS__ and friends, are not available
// while we're still in the initial parse phase.
if (ar->getPhase() == AnalysisResult::ParseAllFiles) {
if ((scalar1 && scalar1->needsTranslation()) ||
(scalar2 && scalar2->needsTranslation())) {
return ExpressionPtr();
}
}
if (!Option::WholeProgram || !Option::ParseTimeOpts) {
// In the VM, don't optimize __CLASS__ if within a trait, since
// __CLASS__ is not resolved yet.
ClassScopeRawPtr clsScope = getOriginalClass();
if (clsScope && clsScope->isTrait()) {
ScalarExpressionPtr scalar1 =
dynamic_pointer_cast<ScalarExpression>(m_exp1);
ScalarExpressionPtr scalar2 =
dynamic_pointer_cast<ScalarExpression>(m_exp2);
if ((scalar1 && scalar1->getType() == T_CLASS_C) ||
(scalar2 && scalar2->getType() == T_CLASS_C)) {
return ExpressionPtr();
@@ -565,7 +581,7 @@ ExpressionPtr BinaryOpExpression::foldConst(AnalysisResultConstPtr ar) {
return makeScalarExpression(ar, result);
} catch (...) {
}
} else {
} else if (ar->getPhase() != AnalysisResult::ParseAllFiles) {
switch (m_op) {
case T_LOGICAL_AND:
case T_BOOLEAN_AND:
@@ -108,6 +108,19 @@ void ScalarExpression::toLower(bool funcCall /* = false */) {
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
bool ScalarExpression::needsTranslation() const {
switch (m_type) {
case T_LINE:
case T_NS_C:
case T_CLASS_C:
case T_METHOD_C:
case T_FUNC_C:
return true;
default:
return false;
}
}
void ScalarExpression::analyzeProgram(AnalysisResultPtr ar) {
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
string id = Util::toLower(getIdentifier());
@@ -46,6 +46,7 @@ public:
virtual std::string getLiteralString() const;
std::string getOriginalLiteralString() const;
std::string getLiteralStringImpl(bool original) const;
bool needsTranslation() const;
TypePtr inferenceImpl(AnalysisResultConstPtr ar, TypePtr type,
bool coerce);
virtual TypePtr inferAndCheck(AnalysisResultPtr ar, TypePtr type,
+4
Ver Arquivo
@@ -663,6 +663,10 @@ void Parser::onBinaryOpExp(Token &out, Token &operand1, Token &operand2,
}
out->exp = bop;
// If the operands are simple enough we can fold this expression right
// here and keep the parse tree smaller.
if (ExpressionPtr optExp = bop->foldConst(m_ar)) out->exp = optExp;
}
void Parser::onQOp(Token &out, Token &exprCond, Token *expYes, Token &expNo) {
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,2 @@
225000
Pass
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -0,0 +1,3 @@
0123456789012345...4567890123456789
Total length: 50000
Pass