private void DoBinaryOperator()

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)
                    }
                }
            }
        }