in AjaxMinDll/JavaScript/AnalyzeNodeVisitor.cs [2721:2937]
public override void Visit(IfNode node)
{
if (node != null)
{
// recurse....
base.Visit(node);
// now check to see if the two branches are now empty.
// if they are, null them out.
if (node.TrueBlock != null && node.TrueBlock.Count == 0)
{
node.TrueBlock = null;
}
if (node.FalseBlock != null && node.FalseBlock.Count == 0)
{
node.FalseBlock = null;
}
if (node.Condition != null && node.Condition.IsDebugOnly)
{
node.Condition = new ConstantWrapper(0, PrimitiveType.Number, node.Condition.Context);
node.TrueBlock = null;
}
if (node.TrueBlock != null && node.FalseBlock != null)
{
// neither true block nor false block is null.
// if they're both expressions, convert them to a condition operator
if (node.TrueBlock.IsExpression && node.FalseBlock.IsExpression
&& m_parser.Settings.IsModificationAllowed(TreeModifications.IfExpressionsToExpression))
{
// if this statement has both true and false blocks, and they are both expressions,
// then we can simplify this to a conditional expression.
// because the blocks are expressions, we know they only have ONE statement in them,
// so we can just dereference them directly.
Conditional conditional;
var logicalNot = new LogicalNot(node.Condition, m_parser.Settings);
if (logicalNot.Measure() < 0)
{
// applying a logical-not makes the condition smaller -- reverse the branches
logicalNot.Apply();
conditional = new Conditional(node.Context)
{
Condition = node.Condition,
TrueExpression = node.FalseBlock[0],
FalseExpression = node.TrueBlock[0]
};
}
else
{
// regular order
conditional = new Conditional(node.Context)
{
Condition = node.Condition,
TrueExpression = node.TrueBlock[0],
FalseExpression = node.FalseBlock[0]
};
}
node.Parent.ReplaceChild(
node,
conditional);
Optimize(conditional);
}
else
{
// see if logical-notting the condition produces something smaller
var logicalNot = new LogicalNot(node.Condition, m_parser.Settings);
if (logicalNot.Measure() < 0)
{
// it does -- not the condition and swap the branches
logicalNot.Apply();
node.SwapBranches();
}
// see if the true- and false-branches each contain only a single statement
if (node.TrueBlock.Count == 1 && node.FalseBlock.Count == 1)
{
// they do -- see if the true-branch's statement is a return-statement
var trueReturn = node.TrueBlock[0] as ReturnNode;
if (trueReturn != null && trueReturn.Operand != null)
{
// it is -- see if the false-branch is also a return statement
var falseReturn = node.FalseBlock[0] as ReturnNode;
if (falseReturn != null && falseReturn.Operand != null)
{
// transform: if(cond)return expr1;else return expr2 to return cond?expr1:expr2
var conditional = new Conditional(node.Condition.Context.FlattenToStart())
{
Condition = node.Condition,
TrueExpression = trueReturn.Operand,
FalseExpression = falseReturn.Operand
};
// create a new return node from the conditional and replace
// our if-node with it
var returnNode = new ReturnNode(node.Context)
{
Operand = conditional
};
node.Parent.ReplaceChild(
node,
returnNode);
Optimize(conditional);
}
}
}
}
}
else if (node.FalseBlock != null)
{
// true block must be null.
// if there is no true branch but a false branch, then
// put a not on the condition and move the false branch to the true branch.
if (node.FalseBlock.IsExpression
&& m_parser.Settings.IsModificationAllowed(TreeModifications.IfConditionCallToConditionAndCall))
{
// if (cond); else expr ==> cond || expr
// but first -- which operator to use? if(a);else b --> a||b, and if(!a);else b --> a&&b
// so determine which one is smaller: a or !a
// assume we'll use the logical-or, since that doesn't require changing the condition
var newOperator = JSToken.LogicalOr;
var logicalNot = new LogicalNot(node.Condition, m_parser.Settings);
if (logicalNot.Measure() < 0)
{
// !a is smaller, so apply it and use the logical-or operator
logicalNot.Apply();
newOperator = JSToken.LogicalAnd;
}
var binaryOp = new BinaryOperator(node.Context)
{
Operand1 = node.Condition,
Operand2 = node.FalseBlock[0],
OperatorToken = newOperator,
};
// we don't need to analyse this new node because we've already analyzed
// the pieces parts as part of the if. And this visitor's method for the BinaryOperator
// doesn't really do anything else. Just replace our current node with this
// new node
node.Parent.ReplaceChild(node, binaryOp);
}
else if (m_parser.Settings.IsModificationAllowed(TreeModifications.IfConditionFalseToIfNotConditionTrue))
{
// logical-not the condition
// if(cond);else stmt ==> if(!cond)stmt
var logicalNot = new LogicalNot(node.Condition, m_parser.Settings);
logicalNot.Apply();
// and swap the branches
node.SwapBranches();
}
}
else if (node.TrueBlock != null)
{
// false block must be null
if (node.TrueBlock.IsExpression
&& m_parser.Settings.IsModificationAllowed(TreeModifications.IfConditionCallToConditionAndCall))
{
// convert the if-node to an expression
IfConditionExpressionToExpression(node, node.TrueBlock[0]);
}
}
else if (m_parser.Settings.IsModificationAllowed(TreeModifications.IfEmptyToExpression))
{
// NEITHER branches have anything now!
// as long as the condition doesn't
// contain calls or assignments, we should be able to completely delete
// the statement altogether rather than changing it to an expression
// statement on the condition.
// but how do we KNOW there are no side-effects?
// if the condition is a constant or operations on constants, delete it.
// or if the condition itself is a debugger statement -- a call, lookup, or member.
var remove = node.Condition == null || node.Condition.IsConstant || node.Condition.IsDebugOnly;
if (remove)
{
// we're pretty sure there are no side-effects; remove it altogether
node.Parent.ReplaceChild(node, null);
}
else
{
// We don't know what it is and what the side-effects may be, so
// just change this statement into an expression statement by replacing us with
// the expression
// no need to analyze -- we already recursed
node.Parent.ReplaceChild(node, node.Condition);
}
}
if (node.FalseBlock == null
&& node.TrueBlock != null
&& node.TrueBlock.Count == 1
&& m_parser.Settings.IsModificationAllowed(TreeModifications.CombineNestedIfs))
{
var nestedIf = node.TrueBlock[0] as IfNode;
if (nestedIf != null && nestedIf.FalseBlock == null)
{
// we have nested if-blocks.
// transform if(cond1)if(cond2){...} to if(cond1&&cond2){...}
// change the first if-statement's condition to be cond1&&cond2
// move the nested if-statement's true block to the outer if-statement
node.Condition = new BinaryOperator(node.Condition.Context.FlattenToStart())
{
Operand1 = node.Condition,
Operand2 = nestedIf.Condition,
OperatorToken = JSToken.LogicalAnd
};
node.TrueBlock = nestedIf.TrueBlock;
}
}
}
}