in AjaxMinDll/JavaScript/EvaluateLiteralVisitor.cs [1817:2002]
private void DoBinaryOperator(BinaryOperator node)
{
if (m_parser.Settings.EvalLiteralExpressions)
{
// if this is an assign operator, an in, or an instanceof, then we won't
// try to evaluate it
if (!node.IsAssign && node.OperatorToken != JSToken.In && node.OperatorToken != JSToken.InstanceOf)
{
if (node.OperatorToken == JSToken.StrictEqual || node.OperatorToken == JSToken.StrictNotEqual)
{
// the operator is a strict equality (or not-equal).
// check the primitive types of the two operands -- if they are known but not the same, we can
// shortcut the whole process by just replacing this node with a boolean literal.
var leftType = node.Operand1.FindPrimitiveType();
if (leftType != PrimitiveType.Other)
{
var rightType = node.Operand2.FindPrimitiveType();
if (rightType != PrimitiveType.Other)
{
// both sides are known
if (leftType != rightType)
{
// they are not the same type -- replace with a boolean and bail
ReplaceNodeWithLiteral(
node,
new ConstantWrapper(node.OperatorToken == JSToken.StrictEqual ? false : true, PrimitiveType.Boolean, node.Context));
return;
}
// they are the same type -- we can change the operator to simple equality/not equality
node.OperatorToken = node.OperatorToken == JSToken.StrictEqual ? JSToken.Equal : JSToken.NotEqual;
}
}
}
// see if the left operand is a literal number, boolean, string, or null
ConstantWrapper left = node.Operand1 as ConstantWrapper;
if (left != null)
{
if (node.OperatorToken == JSToken.Comma)
{
// the comma operator evaluates the left, then evaluates the right and returns it.
// but if the left is a literal, evaluating it doesn't DO anything, so we can replace the
// entire operation with the right-hand operand
ConstantWrapper rightConstant = node.Operand2 as ConstantWrapper;
if (rightConstant != null)
{
// we'll replace the operator with the right-hand operand, BUT it's a constant, too.
// first check to see if replacing this node with a constant will result in creating
// a member-bracket operator that can be turned into a member-dot. If it is, then that
// method will handle the replacement. But if it doesn't, then we should just replace
// the comma with the right-hand operand.
if (!ReplaceMemberBracketWithDot(node, rightConstant))
{
ReplaceNodeWithLiteral(node, rightConstant);
}
}
else if (node is CommaOperator)
{
// this is a collection of expression statements that we've joined together as
// an extended comma operator.
var list = node.Operand2 as AstNodeList;
if (list == null)
{
// not a list, just a single item, so we can just
// replace this entire node with the one element
ReplaceNodeCheckParens(node, node.Operand2);
}
else if (list.Count == 1)
{
// If the list has a single element, then we can just
// replace this entire node with the one element
ReplaceNodeCheckParens(node, list[0]);
}
else if (list.Count == 0)
{
// the recursion ended up emptying the list, so we can just delete
// this node altogether
ReplaceNodeCheckParens(node, null);
}
else
{
// more than one item in the list
// move the first item from the list to the left-hand side
var firstItem = list[0];
list.RemoveAt(0);
node.Operand1 = firstItem;
// if there's only one item left in the list, we can get rid of the
// extra list node and make it just the remaining node
if (list.Count == 1)
{
firstItem = list[0];
list.RemoveAt(0);
node.Operand2 = firstItem;
}
}
}
else
{
// replace the comma operator with the right-hand operand
ReplaceNodeCheckParens(node, node.Operand2);
}
}
else
{
// see if the right operand is a literal number, boolean, string, or null
ConstantWrapper right = node.Operand2 as ConstantWrapper;
if (right != null)
{
// then they are both constants and we can evaluate the operation
EvalThisOperator(node, left, right);
}
else
{
// see if the right is a binary operator that can be combined with ours
BinaryOperator rightBinary = node.Operand2 as BinaryOperator;
if (rightBinary != null)
{
ConstantWrapper rightLeft = rightBinary.Operand1 as ConstantWrapper;
if (rightLeft != null)
{
// eval our left and the right-hand binary's left and put the combined operation as
// the child of the right-hand binary
EvalToTheRight(node, left, rightLeft, rightBinary);
}
else
{
ConstantWrapper rightRight = rightBinary.Operand2 as ConstantWrapper;
if (rightRight != null)
{
EvalFarToTheRight(node, left, rightRight, rightBinary);
}
}
}
}
}
}
else
{
// left is not a constantwrapper. See if the right is
ConstantWrapper right = node.Operand2 as ConstantWrapper;
if (right != null)
{
// the right is a constant. See if the the left is a binary operator...
BinaryOperator leftBinary = node.Operand1 as BinaryOperator;
if (leftBinary != null)
{
// ...with a constant on the right, and the operators can be combined
ConstantWrapper leftRight = leftBinary.Operand2 as ConstantWrapper;
if (leftRight != null)
{
EvalToTheLeft(node, right, leftRight, leftBinary);
}
else
{
ConstantWrapper leftLeft = leftBinary.Operand1 as ConstantWrapper;
if (leftLeft != null)
{
EvalFarToTheLeft(node, right, leftLeft, leftBinary);
}
}
}
else if (m_parser.Settings.IsModificationAllowed(TreeModifications.SimplifyStringToNumericConversion))
{
// see if it's a lookup and this is a minus operation and the constant is a zero
Lookup lookup = node.Operand1 as Lookup;
if (lookup != null && node.OperatorToken == JSToken.Minus && right.IsIntegerLiteral && right.ToNumber() == 0)
{
// okay, so we have "lookup - 0"
// this is done frequently to force a value to be numeric.
// There is an easier way: apply the unary + operator to it.
var unary = new UnaryOperator(node.Context)
{
Operand = lookup,
OperatorToken = JSToken.Plus
};
ReplaceNodeCheckParens(node, unary);
}
}
}
// TODO: shouldn't we check if they BOTH are binary operators? (a*6)*(5*b) ==> a*30*b (for instance)
}
}
}
}