public override void Visit(JsBinaryOperator node)
 {
     if (node != null)
     {
         // if this isn't a comma-operator, just recurse normal.
         // if it is and this is the root block (parent is null) or a function block
         // or there's already more than one statement in the block, we will want to possibly break
         // this comma-operator expression statement into separate expression statements.
         JsBlock parentBlock;
         if (node.OperatorToken == JsToken.Comma
             && m_parser.Settings.IsModificationAllowed(JsTreeModifications.UnfoldCommaExpressionStatements)
             && ((parentBlock = node.Parent as JsBlock) != null)
             && (parentBlock.Parent == null
                 || parentBlock.Parent is JsFunctionObject
                 || parentBlock.Parent is JsTryNode
                 || parentBlock.Parent is JsSwitchCase
                 || parentBlock.Count > 1))
         {
             // possibly break this one comma statement into multiple statements and recurse
             PossiblyBreakExpressionStatement(node, parentBlock);
         }
         else
         {
             // just recurse it normally
             base.Visit(node);
         }
     }
 }
 public override void Visit(JsBinaryOperator node)
 {
     if (node != null)
     {
         // if this isn't a comma-operator, just recurse normal.
         // if it is and this is the root block (parent is null) or a function block
         // or there's already more than one statement in the block, we will want to possibly break
         // this comma-operator expression statement into separate expression statements.
         JsBlock parentBlock;
         if (node.OperatorToken == JsToken.Comma &&
             m_parser.Settings.IsModificationAllowed(JsTreeModifications.UnfoldCommaExpressionStatements) &&
             ((parentBlock = node.Parent as JsBlock) != null) &&
             (parentBlock.Parent == null ||
              parentBlock.Parent is JsFunctionObject ||
              parentBlock.Parent is JsTryNode ||
              parentBlock.Parent is JsSwitchCase ||
              parentBlock.Count > 1))
         {
             // possibly break this one comma statement into multiple statements and recurse
             PossiblyBreakExpressionStatement(node, parentBlock);
         }
         else
         {
             // just recurse it normally
             base.Visit(node);
         }
     }
 }
        private void PossiblyBreakExpressionStatement(JsBinaryOperator node, JsBlock parentBlock)
        {
            var nodeList = node.Operand2 as JsAstNodeList;

            if (nodeList != null)
            {
                PossiblyBreakExpressionList(node, parentBlock, nodeList);
            }
            else
            {
                //  not a list
                if (CanBeBroken(node.Operand2))
                {
                    // flatten the operator. We have to explicitly recurse the left-hand side.
                    var temp = node.Operand1;
                    parentBlock.ReplaceChild(node, temp);
                    parentBlock.InsertAfter(temp, node.Operand2);
                    temp.Accept(this);
                }
                else
                {
                    // no change; just recurse normally
                    base.Visit(node);
                }
            }
        }
 private static void RotateOpeator(JsBinaryOperator node, JsAstNodeList rightSide)
 {
     if (rightSide.Count == 0)
     {
         // the list is empty -- remove the node altogether
         node.Parent.ReplaceChild(node, null);
     }
     else if (rightSide.Count == 1)
     {
         // the list has only one item -- replace the node with the one item
         node.Parent.ReplaceChild(node, rightSide[0]);
     }
     else if (rightSide.Count == 2)
     {
         // there are only two items -- rotate the first to the left-hand side
         // and replace the right-hand side with the second item
         node.Operand1 = rightSide[0];
         node.Operand2 = rightSide[1];
     }
     else
     {
         // there will still be more than one left in the list after we peel off the
         // first one. rotate the first item to the left-hand side
         var temp = rightSide[0];
         rightSide.RemoveAt(0);
         node.Operand1 = temp;
     }
 }
 public void Visit(JsBinaryOperator node)
 {
     // if there's a left-hand operand, recurse into it
     if (node != null && node.Operand1 != null)
     {
         node.Operand1.Accept(this);
     }
 }
        private void PossiblyBreakExpressionList(JsBinaryOperator node, JsBlock parentBlock, JsAstNodeList nodeList)
        {
            // if the first item can be broken, then we an break it and be done.
            // otherwise we're going to have to walk until we find a breaking place
            if (CanBeBroken(nodeList[0]))
            {
                // break the first item. insert the left-hand side at our position and
                // recurse it. Then rotate the node.
                var index = parentBlock.IndexOf(node);
                var temp  = node.Operand1;
                RotateOpeator(node, nodeList);
                parentBlock.Insert(index, temp);

                // assumes nothing will cause the node to be deleted, because then it
                // would cause us to miss the following item
                temp.Accept(this);
            }
            else
            {
                // the first one can't be broken, so find the first one that can (if any)
                for (var ndx = 1; ndx < nodeList.Count; ++ndx)
                {
                    if (CanBeBroken(nodeList[ndx]))
                    {
                        if (ndx == 1)
                        {
                            // the second item is where we are breaking it, so we're going to pull
                            // the first item, replace the list with that first item, then insert
                            // a new comma operator after the current node
                            var temp = nodeList[0];
                            nodeList.RemoveAt(0);
                            node.Operand2 = temp;

                            // if there's nothing left, then let it die. Otherwise split off
                            // the remainder and insert after the current item.
                            if (nodeList.Count > 0)
                            {
                                parentBlock.InsertAfter(node, CreateSplitNodeFromEnd(nodeList, 0));
                            }
                        }
                        else
                        {
                            // split off items from the index where we want to split, and insert
                            // it after the current node and leave the node list where it is.
                            parentBlock.InsertAfter(node, CreateSplitNodeFromEnd(nodeList, ndx));
                        }

                        // and now that we've broken it, bail.
                        break;
                    }
                }

                // regardless if anything changed, recurse this node now
                base.Visit(node);
            }
        }
        public override void Visit(JsBinaryOperator node)
        {
            if (node != null)
            {
                // depth-first
                base.Visit(node);

                // then evaluate.
                // do it in a separate method than this one because if this method
                // allocates a lot of bytes on the stack, we'll overflow our stack for
                // code that has lots of expression statements that get converted into one
                // BIG, uber-nested set of comma operators.
                DoBinaryOperator(node);
            }
        }
Ejemplo n.º 8
0
 public override void Visit(JsBinaryOperator node)
 {
     if (node != null)
     {
         if (m_measure)
         {
             // measure
             MeasureBinaryOperator(node);
         }
         else
         {
             // convert
             ConvertBinaryOperator(node);
         }
     }
 }
Ejemplo n.º 9
0
 public override void Visit(JsBinaryOperator node)
 {
     if (node != null)
     {
         if (m_measure)
         {
             // measure
             MeasureBinaryOperator(node);
         }
         else
         {
             // convert
             ConvertBinaryOperator(node);
         }
     }
 }
        public void Visit(JsBinaryOperator node)
        {
            if (node != null)
            {
                if (node.Operand1 != null)
                {
                    node.Operand1.Accept(this);
                }

                if (node.Operand2 != null)
                {
                    node.Operand2.Accept(this);
                }

                node.Index = NextOrderIndex;
            }
        }
Ejemplo n.º 11
0
        private void MeasureBinaryOperator(JsBinaryOperator node)
        {
            // depending on the operator, calculate the potential difference in length
            switch (node.OperatorToken)
            {
                case JsToken.Equal:
                case JsToken.NotEqual:
                case JsToken.StrictEqual:
                case JsToken.StrictNotEqual:
                    // these operators can be turned into a logical not without any
                    // delta in code size. == becomes !=, etc.
                    break;

                case JsToken.LessThan:
                case JsToken.GreaterThan:
                // these operators would add another character when turnbed into a not.
                // for example, < becomes >=, etc
                //++m_delta;
                //break;

                case JsToken.LessThanEqual:
                case JsToken.GreaterThanEqual:
                // these operators would subtract another character when turnbed into a not.
                // for example, <= becomes >, etc
                //--m_delta;
                //break;

                case JsToken.Assign:
                case JsToken.PlusAssign:
                case JsToken.MinusAssign:
                case JsToken.MultiplyAssign:
                case JsToken.DivideAssign:
                case JsToken.ModuloAssign:
                case JsToken.BitwiseAndAssign:
                case JsToken.BitwiseOrAssign:
                case JsToken.BitwiseXorAssign:
                case JsToken.LeftShiftAssign:
                case JsToken.RightShiftAssign:
                case JsToken.UnsignedRightShiftAssign:
                case JsToken.BitwiseAnd:
                case JsToken.BitwiseOr:
                case JsToken.BitwiseXor:
                case JsToken.Divide:
                case JsToken.Multiply:
                case JsToken.Modulo:
                case JsToken.Minus:
                case JsToken.Plus:
                case JsToken.LeftShift:
                case JsToken.RightShift:
                case JsToken.UnsignedRightShift:
                case JsToken.In:
                case JsToken.InstanceOf:
                    // these operators have no logical not, which means we need to wrap them in
                    // a unary logical-not operator. And since they have a lower precedence than
                    // the unary logical-not, they'll have to be wrapped in parens. So that means
                    // logical-not'ing these guys adds three characters
                    m_delta += 3;
                    break;

                case JsToken.Comma:
                    // to logical-not a comma-operator, we just need to logical-not the
                    // right-hand side
                    node.Operand2.Accept(this);
                    break;

                case JsToken.LogicalAnd:
                case JsToken.LogicalOr:
                    if (node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock))
                    {
                        // if the parent is a block, then this is a simple expression statement:
                        // expr1 || expr2; or expr1 && expr2; If so, then the result isn't
                        // used anywhere and we're just using the || or && operator as a
                        // shorter if-statement. So we don't need to negate the right-hand
                        // side, just the left-hand side.
                        if (node.Operand1 != null)
                        {
                            node.Operand1.Accept(this);
                        }
                    }
                    else
                    {
                        // the logical-not of a logical-and or logical-or operation is the
                        // other operation against the not of each operand. Since the opposite
                        // operator is the same length as this operator, then we just need
                        // to recurse both operands to find the true delta.
                        if (node.Operand1 != null)
                        {
                            node.Operand1.Accept(this);
                        }

                        if (node.Operand2 != null)
                        {
                            node.Operand2.Accept(this);
                        }
                    }
                    break;
            }
        }
 private void PossiblyBreakExpressionStatement(JsBinaryOperator node, JsBlock parentBlock)
 {
     var nodeList = node.Operand2 as JsAstNodeList;
     if (nodeList != null)
     {
         PossiblyBreakExpressionList(node, parentBlock, nodeList);
     }
     else
     {
         //  not a list
         if (CanBeBroken(node.Operand2))
         {
             // flatten the operator. We have to explicitly recurse the left-hand side.
             var temp = node.Operand1;
             parentBlock.ReplaceChild(node, temp);
             parentBlock.InsertAfter(temp, node.Operand2);
             temp.Accept(this);
         }
         else
         {
             // no change; just recurse normally
             base.Visit(node);
         }
     }
 }
Ejemplo n.º 13
0
        private void ConvertBinaryOperator(JsBinaryOperator node)
        {
            // depending on the operator, perform whatever we need to do to apply a logical
            // not to the operation
            switch (node.OperatorToken)
            {
                case JsToken.Equal:
                    node.OperatorToken = JsToken.NotEqual;
                    break;

                case JsToken.NotEqual:
                    node.OperatorToken = JsToken.Equal;
                    break;

                case JsToken.StrictEqual:
                    node.OperatorToken = JsToken.StrictNotEqual;
                    break;

                case JsToken.StrictNotEqual:
                    node.OperatorToken = JsToken.StrictEqual;
                    break;

                case JsToken.LessThan:
                //node.OperatorToken = JSToken.GreaterThanEqual;
                //break;

                case JsToken.GreaterThan:
                //node.OperatorToken = JSToken.LessThanEqual;
                //break;

                case JsToken.LessThanEqual:
                //node.OperatorToken = JSToken.GreaterThan;
                //break;

                case JsToken.GreaterThanEqual:
                //node.OperatorToken = JSToken.LessThan;
                //break;

                case JsToken.Assign:
                case JsToken.PlusAssign:
                case JsToken.MinusAssign:
                case JsToken.MultiplyAssign:
                case JsToken.DivideAssign:
                case JsToken.ModuloAssign:
                case JsToken.BitwiseAndAssign:
                case JsToken.BitwiseOrAssign:
                case JsToken.BitwiseXorAssign:
                case JsToken.LeftShiftAssign:
                case JsToken.RightShiftAssign:
                case JsToken.UnsignedRightShiftAssign:
                case JsToken.BitwiseAnd:
                case JsToken.BitwiseOr:
                case JsToken.BitwiseXor:
                case JsToken.Divide:
                case JsToken.Multiply:
                case JsToken.Modulo:
                case JsToken.Minus:
                case JsToken.Plus:
                case JsToken.LeftShift:
                case JsToken.RightShift:
                case JsToken.UnsignedRightShift:
                case JsToken.In:
                case JsToken.InstanceOf:
                    WrapWithLogicalNot(node);
                    break;

                case JsToken.Comma:
                    // to logical-not a comma-operator, we just need to logical-not the
                    // right-hand side
                    node.Operand2.Accept(this);
                    break;

                case JsToken.LogicalAnd:
                case JsToken.LogicalOr:
                    if (node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock))
                    {
                        // if the parent is a block, then this is a simple expression statement:
                        // expr1 || expr2; or expr1 && expr2; If so, then the result isn't
                        // used anywhere and we're just using the || or && operator as a
                        // shorter if-statement. So we don't need to negate the right-hand
                        // side, just the left-hand side.
                        if (node.Operand1 != null)
                        {
                            node.Operand1.Accept(this);
                        }
                    }
                    else
                    {
                        // the logical-not of a logical-and or logical-or operation is the
                        // other operation against the not of each operand. Since the opposite
                        // operator is the same length as this operator, then we just need
                        // to recurse both operands and swap the operator token
                        if (node.Operand1 != null)
                        {
                            node.Operand1.Accept(this);
                        }

                        if (node.Operand2 != null)
                        {
                            node.Operand2.Accept(this);
                        }
                    }
                    node.OperatorToken = node.OperatorToken == JsToken.LogicalAnd ? JsToken.LogicalOr : JsToken.LogicalAnd;
                    break;
            }
        }
Ejemplo n.º 14
0
 public void Visit(JsBinaryOperator node)
 {
     // lesser precedence than the new operator; use parens
     m_needsParens = true;
 }
        private void CombineTwoExpressions(JsBlock node, int ndx)
        {
            var prevBinary = node[ndx - 1] as JsBinaryOperator;
            var curBinary = node[ndx] as JsBinaryOperator;
            JsLookup lookup;
            if (prevBinary != null
                && curBinary != null
                && prevBinary.IsAssign
                && curBinary.IsAssign
                && curBinary.OperatorToken != JsToken.Assign
                && (lookup = curBinary.Operand1 as JsLookup) != null
                && prevBinary.Operand1.IsEquivalentTo(curBinary.Operand1))
            {
                if (prevBinary.OperatorToken == JsToken.Assign)
                {
                    // transform: lookup=expr1;lookup[OP]=expr2;  ==>  lookup=expr1[OP]expr2
                    var binOp = new JsBinaryOperator(prevBinary.Operand2.Context.Clone().CombineWith(curBinary.Operand2.Context), prevBinary.Parser)
                    {
                        Operand1 = prevBinary.Operand2,
                        Operand2 = curBinary.Operand2,
                        OperatorToken = JsScanner.StripAssignment(curBinary.OperatorToken),
                        OperatorContext = curBinary.OperatorContext
                    };
                    prevBinary.Operand2 = binOp;

                    // we are removing the second lookup, so clean up the reference on the field
                    if (lookup.VariableField != null)
                    {
                        lookup.VariableField.References.Remove(lookup);
                    }

                    // and remove the current assignment expression (everything was combined into the previous)
                    node[ndx] = null;
                }
                else
                {
                    // there's lots of ins-and-outs in terms of strings versus numerics versus precedence and all
                    // sorts of stuff. I need to iron this out a little better, but until then, just combine with a comma.
                    // transform: expr1;expr2  ==>  expr1,expr2
                    var binOp = JsCommaOperator.CombineWithComma(prevBinary.Context.Clone().CombineWith(curBinary.Context), m_parser, prevBinary, curBinary);

                    // replace the previous node and delete the current
                    node[ndx - 1] = binOp;
                    node[ndx] = null;
                }
            }
            else
            {
                // transform: expr1;expr2 to expr1,expr2
                // use the special comma operator object so we can handle it special
                // and don't create stack-breakingly deep trees
                var binOp = JsCommaOperator.CombineWithComma(node[ndx - 1].Context.Clone().CombineWith(node[ndx].Context), m_parser, node[ndx - 1], node[ndx]);

                // replace the current node and delete the previous
                node[ndx] = binOp;
                node[ndx - 1] = null;
            }
        }
Ejemplo n.º 16
0
        private void ConvertBinaryOperator(JsBinaryOperator node)
        {
            // depending on the operator, perform whatever we need to do to apply a logical
            // not to the operation
            switch (node.OperatorToken)
            {
            case JsToken.Equal:
                node.OperatorToken = JsToken.NotEqual;
                break;

            case JsToken.NotEqual:
                node.OperatorToken = JsToken.Equal;
                break;

            case JsToken.StrictEqual:
                node.OperatorToken = JsToken.StrictNotEqual;
                break;

            case JsToken.StrictNotEqual:
                node.OperatorToken = JsToken.StrictEqual;
                break;

            case JsToken.LessThan:
            //node.OperatorToken = JSToken.GreaterThanEqual;
            //break;

            case JsToken.GreaterThan:
            //node.OperatorToken = JSToken.LessThanEqual;
            //break;

            case JsToken.LessThanEqual:
            //node.OperatorToken = JSToken.GreaterThan;
            //break;

            case JsToken.GreaterThanEqual:
            //node.OperatorToken = JSToken.LessThan;
            //break;

            case JsToken.Assign:
            case JsToken.PlusAssign:
            case JsToken.MinusAssign:
            case JsToken.MultiplyAssign:
            case JsToken.DivideAssign:
            case JsToken.ModuloAssign:
            case JsToken.BitwiseAndAssign:
            case JsToken.BitwiseOrAssign:
            case JsToken.BitwiseXorAssign:
            case JsToken.LeftShiftAssign:
            case JsToken.RightShiftAssign:
            case JsToken.UnsignedRightShiftAssign:
            case JsToken.BitwiseAnd:
            case JsToken.BitwiseOr:
            case JsToken.BitwiseXor:
            case JsToken.Divide:
            case JsToken.Multiply:
            case JsToken.Modulo:
            case JsToken.Minus:
            case JsToken.Plus:
            case JsToken.LeftShift:
            case JsToken.RightShift:
            case JsToken.UnsignedRightShift:
            case JsToken.In:
            case JsToken.InstanceOf:
                WrapWithLogicalNot(node);
                break;

            case JsToken.Comma:
                // to logical-not a comma-operator, we just need to logical-not the
                // right-hand side
                node.Operand2.Accept(this);
                break;

            case JsToken.LogicalAnd:
            case JsToken.LogicalOr:
                if (node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock))
                {
                    // if the parent is a block, then this is a simple expression statement:
                    // expr1 || expr2; or expr1 && expr2; If so, then the result isn't
                    // used anywhere and we're just using the || or && operator as a
                    // shorter if-statement. So we don't need to negate the right-hand
                    // side, just the left-hand side.
                    if (node.Operand1 != null)
                    {
                        node.Operand1.Accept(this);
                    }
                }
                else
                {
                    // the logical-not of a logical-and or logical-or operation is the
                    // other operation against the not of each operand. Since the opposite
                    // operator is the same length as this operator, then we just need
                    // to recurse both operands and swap the operator token
                    if (node.Operand1 != null)
                    {
                        node.Operand1.Accept(this);
                    }

                    if (node.Operand2 != null)
                    {
                        node.Operand2.Accept(this);
                    }
                }
                node.OperatorToken = node.OperatorToken == JsToken.LogicalAnd ? JsToken.LogicalOr : JsToken.LogicalAnd;
                break;
            }
        }
Ejemplo n.º 17
0
        public void Visit(JsBinaryOperator node)
        {
            if (node != null)
            {
                var symbol = StartSymbol(node);

                if (node.OperatorToken == JsToken.Comma)
                {
                    // output the left-hand operand, if we have one
                    if (node.Operand1 != null)
                    {
                        node.Operand1.Accept(this);
                        SetContextOutputPosition(node.Context, node.Operand1.Context);

                        // if we don't have a right-hand operator, don't bother with the comma
                        if (node.Operand2 != null)
                        {
                            OutputPossibleLineBreak(',');
                            MarkSegment(node, null, node.Operand1.TerminatingContext);
                            m_startOfStatement = false;

                            // if the parent is a block, then the comma operator is separating
                            // expression statements -- so break it on the line
                            if (node.Parent is JsBlock)
                            {
                                NewLine();
                            }
                            else if (m_settings.OutputMode == MinifierOutputMode.MultipleLines)
                            {
                                OutputPossibleLineBreak(' ');
                            }
                        }
                    }

                    // output the right-hand operator, if we have one
                    if (node.Operand2 != null)
                    {
                        node.Operand2.Accept(this);
                        m_startOfStatement = false;
                    }
                }
                else
                {
                    var ourPrecedence = node.Precedence;
                    var isNoIn = m_noIn;
                    if (isNoIn)
                    {
                        if (node.OperatorToken == JsToken.In)
                        {
                            // we're in a no-in situation, but our operator is an in-operator.
                            // so we need to wrap this operator in parens
                            OutputPossibleLineBreak('(');
                            m_noIn = false;
                        }
                        else
                        {
                            m_noIn = ourPrecedence <= JsOperatorPrecedence.Relational;
                        }
                    }

                    if (node.Operand1 != null)
                    {
                        AcceptNodeWithParens(node.Operand1, node.Operand1.Precedence < ourPrecedence);
                        SetContextOutputPosition(node.Context, node.Operand1.Context);
                    }

                    m_startOfStatement = false;

                    if (m_settings.OutputMode == MinifierOutputMode.MultipleLines)
                    {
                        // treat the comma-operator special, since we combine expression statements
                        // with it very often
                        if (node.OperatorToken != JsToken.Comma)
                        {
                            // anything other than a comma operator has a space before it, too
                            OutputPossibleLineBreak(' ');
                        }

                        Output(OperatorString(node.OperatorToken));
                        MarkSegment(node, null, node.OperatorContext);

                        BreakLine(false);
                        if (!m_onNewLine)
                        {
                            OutputPossibleLineBreak(' ');
                        }
                    }
                    else
                    {
                        Output(OperatorString(node.OperatorToken));
                        MarkSegment(node, null, node.OperatorContext);
                        BreakLine(false);
                    }

                    if (node.OperatorToken == JsToken.Divide)
                    {
                        // add a function that will check if the next character is also
                        // a forward slash. If it is, the output methods will separate them
                        // with a space so they don't get interpreted as the start of a
                        // single-line comment.
                        m_addSpaceIfTrue = c => c == '/';
                    }

                    if (node.Operand2 != null)
                    {
                        var rightPrecedence = node.Operand2.Precedence;
                        var rightNeedsParens = rightPrecedence < ourPrecedence;

                        var rightHandBinary = node.Operand2 as JsBinaryOperator;
                        if (rightHandBinary != null)
                        {
                            // they are BOTH binary expressions. This is where it gets complicated.
                            // because most binary tokens (except assignment) are evaluated from left to right,
                            // if we have a binary expression with the same precedence on the RIGHT, then that means the
                            // developer must've put parentheses around it. For some operators, those parentheses
                            // may not be needed (associative operators like multiply and logical AND or logical OR).
                            // Non-associative operators (divide) will need those parens, so we will want to say they
                            // are a higher relative precedence because of those parentheses.
                            // The plus operator is a special case. It is the same physical token, but it can be two
                            // operations depending on the runtime data: numeric addition or string concatenation.
                            // Because of that ambiguity, let's also calculate the precedence for it as if it were
                            // non-associate as well.
                            // commas never need the parens -- they always evaluate left to right and always return the
                            // right value, so any parens will always be unneccessary.
                            if (ourPrecedence == rightPrecedence
                                && ourPrecedence != JsOperatorPrecedence.Assignment
                                && ourPrecedence != JsOperatorPrecedence.Comma)
                            {
                                if (node.OperatorToken == rightHandBinary.OperatorToken)
                                {
                                    // the tokens are the same and we're not assignment or comma operators.
                                    // so for a few associative operators, we're going to say the relative precedence
                                    // is the same so unneeded parens are removed. But for all others, we'll say the
                                    // right-hand side is a higher precedence so we maintain the sematic structure
                                    // of the expression
                                    switch (node.OperatorToken)
                                    {
                                        case JsToken.Multiply:
                                        case JsToken.BitwiseAnd:
                                        case JsToken.BitwiseXor:
                                        case JsToken.BitwiseOr:
                                        case JsToken.LogicalAnd:
                                        case JsToken.LogicalOr:
                                            // these are the same regardless
                                            rightNeedsParens = false;
                                            break;

                                        // TODO: the plus operator: if we can prove that it is a numeric operator
                                        // or a string operator on BOTH sides, then it can be associative, too. But
                                        // if one side is a string and the other numeric, or if we can't tell at
                                        // compile-time, then we need to preserve the structural precedence.
                                        default:
                                            // all other operators are structurally a lower precedence when they
                                            // are on the right, so they need to be evaluated first
                                            rightNeedsParens = true;
                                            break;
                                    }
                                }
                                else
                                {
                                    // they have the same precedence, but the tokens are different.
                                    // and the developer had purposely put parens around the right-hand side
                                    // to get them on the right (otherwise with the same precedence they
                                    // would've ended up on the left. Keep the parens; must've been done for
                                    // a purpose.
                                    rightNeedsParens = true;
                                }
                            }
                            else
                            {
                                // different precedence -- just base the decision on the relative precedence values
                                rightNeedsParens = rightPrecedence < ourPrecedence;
                            }
                        }

                        m_noIn = isNoIn && ourPrecedence <= JsOperatorPrecedence.Relational;
                        AcceptNodeWithParens(node.Operand2, rightNeedsParens);
                    }

                    if (isNoIn && node.OperatorToken == JsToken.In)
                    {
                        // we're in a no-in situation, but our operator is an in-operator.
                        // so we need to wrap this entire operator in parens
                        OutputPossibleLineBreak(')');
                    }
                    m_noIn = isNoIn;

                    EndSymbol(symbol);
                }
            }
        }
 public void Visit(JsBinaryOperator node)
 {
     // lesser precedence than the new operator; use parens
     m_needsParens = true;
 }
Ejemplo n.º 19
0
 public void Visit(JsBinaryOperator node)
 {
     // invalid! ignore
     IsValid = false;
 }
        private void EvalFarToTheLeft(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator leftOperator)
        {
            if (leftOperator.OperatorToken == JsToken.Minus)
            {
                if (node.OperatorToken == JsToken.Plus)
                {
                    // minus-plus
                    // the minus will be a numeric operator, but if this constant is a string, it will be a
                    // string concatenation and we can't combine it.
                    if (thisConstant.PrimitiveType != JsPrimitiveType.String && thisConstant.PrimitiveType != JsPrimitiveType.Other)
                    {
                        JsConstantWrapper newLiteral = NumericAddition(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            RotateFromRight(node, leftOperator, newLiteral);
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Minus)
                {
                    // minus-minus
                    JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        RotateFromRight(node, leftOperator, newLiteral);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Multiply)
            {
                if (leftOperator.OperatorToken == JsToken.Multiply || leftOperator.OperatorToken == JsToken.Divide)
                {
                    JsConstantWrapper newLiteral = Multiply(otherConstant, thisConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, newLiteral))
                    {
                        RotateFromRight(node, leftOperator, newLiteral);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Divide)
            {
                if (leftOperator.OperatorToken == JsToken.Divide)
                {
                    // divide-divide
                    JsConstantWrapper newLiteral = Divide(otherConstant, thisConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, newLiteral)
                        && newLiteral.ToCode().Length <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                    {
                        RotateFromRight(node, leftOperator, newLiteral);
                    }
                }
                else if (leftOperator.OperatorToken == JsToken.Multiply)
                {
                    // mult-divide
                    JsConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    JsConstantWrapper thisOverOther = Divide(thisConstant, otherConstant);

                    int otherOverThisLength = otherOverThis != null ? otherOverThis.ToCode().Length : int.MaxValue;
                    int thisOverOtherLength = thisOverOther != null ? thisOverOther.ToCode().Length : int.MaxValue;

                    if (otherOverThis != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, otherOverThis)
                        && (thisOverOther == null || otherOverThisLength < thisOverOtherLength))
                    {
                        if (otherOverThisLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            RotateFromRight(node, leftOperator, otherOverThis);
                        }
                    }
                    else if (thisOverOther != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, thisOverOther))
                    {
                        if (thisOverOtherLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // swap the operands
                            leftOperator.SwapOperands();

                            // operator is the opposite
                            leftOperator.OperatorToken = JsToken.Divide;
                            RotateFromLeft(node, leftOperator, thisOverOther);
                        }
                    }
                }
            }
        }
        private void EvalFarToTheRight(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator rightOperator)
        {
            if (rightOperator.OperatorToken == JsToken.Minus)
            {
                if (node.OperatorToken == JsToken.Plus)
                {
                    // plus-minus
                    // our constant cannot be a string, though
                    if (!thisConstant.IsStringLiteral)
                    {
                        JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            RotateFromLeft(node, rightOperator, newLiteral);
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Minus)
                {
                    // minus-minus
                    JsConstantWrapper newLiteral = NumericAddition(thisConstant, otherConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        // but we need to swap the left and right operands first
                        rightOperator.SwapOperands();

                        // then rotate the node up after replacing old with new
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Multiply)
            {
                if (rightOperator.OperatorToken == JsToken.Multiply)
                {
                    // mult-mult
                    JsConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                    {
                        RotateFromLeft(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Divide)
                {
                    // mult-divide
                    JsConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    JsConstantWrapper thisOverOther = Divide(thisConstant, otherConstant);

                    int otherOverThisLength = otherOverThis != null ? otherOverThis.ToCode().Length : int.MaxValue;
                    int thisOverOtherLength = thisOverOther != null ? thisOverOther.ToCode().Length : int.MaxValue;

                    if (otherOverThis != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, otherOverThis)
                        && (thisOverOther == null || otherOverThisLength < thisOverOtherLength))
                    {
                        if (otherOverThisLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // swap the operands, but keep the operator
                            RotateFromLeft(node, rightOperator, otherOverThis);
                        }
                    }
                    else if (thisOverOther != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, thisOverOther))
                    {
                        if (thisOverOtherLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // swap the operands and opposite operator
                            rightOperator.SwapOperands();
                            rightOperator.OperatorToken = JsToken.Multiply;
                            RotateFromRight(node, rightOperator, thisOverOther);
                        }
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Divide)
            {
                if (rightOperator.OperatorToken == JsToken.Multiply)
                {
                    // divide-mult
                    JsConstantWrapper newLiteral = Divide(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral)
                        && newLiteral.ToCode().Length <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                    {
                        // swap the operands
                        rightOperator.SwapOperands();

                        // change the operator
                        rightOperator.OperatorToken = JsToken.Divide;
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Divide)
                {
                    // divide-divide
                    JsConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                    {
                        // but we need to swap the left and right operands first
                        rightOperator.SwapOperands();

                        // then rotate the node up after replacing old with new
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
            }
        }
        public override void Visit(JsBinaryOperator node)
        {
            if (node != null)
            {
                base.Visit(node);

                // see if this operation is subtracting zero from a lookup -- that is typically done to
                // coerce a value to numeric. There's a simpler way: unary plus operator.
                if (node.OperatorToken == JsToken.Minus
                    && m_parser.Settings.IsModificationAllowed(JsTreeModifications.SimplifyStringToNumericConversion))
                {
                    JsLookup lookup = node.Operand1 as JsLookup;
                    if (lookup != null)
                    {
                        JsConstantWrapper right = node.Operand2 as JsConstantWrapper;
                        if (right != null && 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.
                            // transform: lookup - 0   => +lookup
                            var unary = new JsUnaryOperator(node.Context, m_parser)
                                {
                                    Operand = lookup,
                                    OperatorToken = JsToken.Plus
                                };
                            node.Parent.ReplaceChild(node, unary);

                            // because we recursed at the top of this function, we don't need to Analyze
                            // the new Unary node. This visitor's method for UnaryOperator only does something
                            // if the operand is a constant -- and this one is a Lookup. And we already analyzed
                            // the lookup.
                        }
                    }
                }
                else if ((node.OperatorToken == JsToken.StrictEqual || node.OperatorToken == JsToken.StrictNotEqual)
                    && m_parser.Settings.IsModificationAllowed(JsTreeModifications.ReduceStrictOperatorIfTypesAreSame))
                {
                    JsPrimitiveType leftType = node.Operand1.FindPrimitiveType();
                    if (leftType != JsPrimitiveType.Other)
                    {
                        JsPrimitiveType rightType = node.Operand2.FindPrimitiveType();
                        if (leftType == rightType)
                        {
                            // the are the same known types. We can reduce the operators
                            node.OperatorToken = node.OperatorToken == JsToken.StrictEqual ? JsToken.Equal : JsToken.NotEqual;
                        }
                        else if (rightType != JsPrimitiveType.Other)
                        {
                            // they are not the same, but they are both known. We can completely remove the operator
                            // and replace it with true (!==) or false (===).
                            // transform: x !== y   =>   true
                            // transform: x === y   =>   false
                            node.Context.HandleError(JsError.StrictComparisonIsAlwaysTrueOrFalse, false);
                            node.Parent.ReplaceChild(
                                node,
                                new JsConstantWrapper(node.OperatorToken == JsToken.StrictNotEqual, JsPrimitiveType.Boolean, node.Context, m_parser));

                            // because we are essentially removing the node from the AST, be sure to detach any references
                            JsDetachReferences.Apply(node);
                        }
                    }
                }
                else if (node.IsAssign)
                {
                    var lookup = node.Operand1 as JsLookup;
                    if (lookup != null)
                    {
                        if (lookup.VariableField != null && lookup.VariableField.InitializationOnly)
                        {
                            // the field is an initialization-only field -- we should NOT be assigning to it
                            lookup.Context.HandleError(JsError.AssignmentToConstant, true);
                        }
                        else if (m_scopeStack.Peek().UseStrict)
                        {
                            if (lookup.VariableField == null || lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal)
                            {
                                // strict mode cannot assign to undefined fields
                                node.Operand1.Context.HandleError(JsError.StrictModeUndefinedVariable, true);
                            }
                            else if(lookup.VariableField.FieldType == JsFieldType.Arguments
                                || (lookup.VariableField.FieldType == JsFieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0))
                            {
                                // strict mode cannot assign to lookup "eval" or "arguments"
                                node.Operand1.Context.HandleError(JsError.StrictModeInvalidAssign, true);
                            }
                        }
                    }
                }
                else if ((node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock))
                    && (node.OperatorToken == JsToken.LogicalOr || node.OperatorToken == JsToken.LogicalAnd))
                {
                    // this is an expression statement where the operator is || or && -- basically
                    // it's a shortcut for an if-statement:
                    // expr1&&expr2; ==> if(expr1)expr2;
                    // expr1||expr2; ==> if(!expr1)expr2;
                    // let's check to see if the not of expr1 is smaller. If so, we can not the expression
                    // and change the operator
                    var logicalNot = new JsLogicalNot(node.Operand1, node.Parser);
                    if (logicalNot.Measure() < 0)
                    {
                        // it would be smaller! Change it.
                        // transform: expr1&&expr2 => !expr1||expr2
                        // transform: expr1||expr2 => !expr1&&expr2
                        logicalNot.Apply();
                        node.OperatorToken = node.OperatorToken == JsToken.LogicalAnd ? JsToken.LogicalOr : JsToken.LogicalAnd;
                    }
                }
            }
        }
        private void Optimize(JsConditional node)
        {
            // now check to see if the condition starts with a not-operator. If so, we can get rid of it
            // and swap the true/false children
            var unary = node.Condition as JsUnaryOperator;
            if (unary != null && unary.OperatorToken == JsToken.LogicalNot
                && !unary.OperatorInConditionalCompilationComment
                && m_parser.Settings.IsModificationAllowed(JsTreeModifications.IfNotTrueFalseToIfFalseTrue))
            {
                // get rid of the not by replacing it with its operand
                // and swap the branches
                node.Condition = unary.Operand;
                node.SwapBranches();
            }

            // see if the two branches are both assignment operations to the same variable.
            // if so, we can pull the assignment outside the conditional and have the conditional
            // be the assignment
            var trueAssign = node.TrueExpression as JsBinaryOperator;
            if (trueAssign != null && trueAssign.IsAssign)
            {
                var falseAssign = node.FalseExpression as JsBinaryOperator;
                if (falseAssign != null && falseAssign.OperatorToken == trueAssign.OperatorToken)
                {
                    // see if the left-hand-side is equivalent
                    if (trueAssign.Operand1.IsEquivalentTo(falseAssign.Operand1))
                    {
                        // we're going to be getting rid of the left-hand side in the false-block,
                        // so we need to remove any references it may represent
                        JsDetachReferences.Apply(falseAssign.Operand1);

                        // transform: cond?lhs=expr1:lhs=expr2 to lhs=cond?expr1:expr2s
                        var binaryOp = new JsBinaryOperator(node.Context, m_parser)
                            {
                                Operand1 = trueAssign.Operand1,
                                Operand2 = new JsConditional(node.Context, m_parser)
                                {
                                    Condition = node.Condition,
                                    QuestionContext = node.QuestionContext,
                                    TrueExpression = trueAssign.Operand2,
                                    ColonContext = node.ColonContext,
                                    FalseExpression = falseAssign.Operand2
                                },
                                OperatorContext = trueAssign.OperatorContext,
                                OperatorToken = trueAssign.OperatorToken,
                                TerminatingContext = node.TerminatingContext
                            };

                        node.Parent.ReplaceChild(node, binaryOp);
                    }
                }
            }
        }
        private void IfConditionExpressionToExpression(JsIfNode ifNode, JsAstNode expression)
        {
            // but first -- which operator to use? if(a)b --> a&&b, and if(!a)b --> a||b
            // so determine which one is smaller: a or !a
            // assume we'll use the logical-and, since that doesn't require changing the condition
            var newOperator = JsToken.LogicalAnd;
            var logicalNot = new JsLogicalNot(ifNode.Condition, m_parser);
            if (logicalNot.Measure() < 0)
            {
                // !a is smaller, so apply it and use the logical-or operator
                logicalNot.Apply();
                newOperator = JsToken.LogicalOr;
            }

            // because the true block is an expression, we know it must only have
            // ONE statement in it, so we can just dereference it directly.
            var binaryOp = new JsBinaryOperator(ifNode.Context, m_parser)
                {
                    Operand1 = ifNode.Condition,
                    Operand2 = expression,
                    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
            ifNode.Parent.ReplaceChild(ifNode, binaryOp);
        }
        private void PossiblyBreakExpressionList(JsBinaryOperator node, JsBlock parentBlock, JsAstNodeList nodeList)
        {
            // if the first item can be broken, then we an break it and be done.
            // otherwise we're going to have to walk until we find a breaking place
            if (CanBeBroken(nodeList[0]))
            {
                // break the first item. insert the left-hand side at our position and
                // recurse it. Then rotate the node.
                var index = parentBlock.IndexOf(node);
                var temp = node.Operand1;
                RotateOpeator(node, nodeList);
                parentBlock.Insert(index, temp);

                // assumes nothing will cause the node to be deleted, because then it
                // would cause us to miss the following item
                temp.Accept(this);
            }
            else
            {
                // the first one can't be broken, so find the first one that can (if any)
                for (var ndx = 1; ndx < nodeList.Count; ++ndx)
                {
                    if (CanBeBroken(nodeList[ndx]))
                    {
                        if (ndx == 1)
                        {
                            // the second item is where we are breaking it, so we're going to pull
                            // the first item, replace the list with that first item, then insert
                            // a new comma operator after the current node
                            var temp = nodeList[0];
                            nodeList.RemoveAt(0);
                            node.Operand2 = temp;

                            // if there's nothing left, then let it die. Otherwise split off
                            // the remainder and insert after the current item.
                            if (nodeList.Count > 0)
                            {
                                parentBlock.InsertAfter(node, CreateSplitNodeFromEnd(nodeList, 0));
                            }
                        }
                        else
                        {
                            // split off items from the index where we want to split, and insert
                            // it after the current node and leave the node list where it is.
                            parentBlock.InsertAfter(node, CreateSplitNodeFromEnd(nodeList, ndx));
                        }

                        // and now that we've broken it, bail.
                        break;
                    }
                }

                // regardless if anything changed, recurse this node now
                base.Visit(node);
            }
        }
        private static bool AreAssignmentsInVar(JsBinaryOperator binaryOp, JsVar varStatement)
        {
            bool areAssignmentsInVar = false;

            if (binaryOp != null)
            {
                // we only want to pop positive for the simple assign (=). If it's any of the
                // complex assigns (+=, -=, etc) then we don't want to combine them.
                if (binaryOp.OperatorToken == JsToken.Assign)
                {
                    // see if the left-hand side is a simple lookup
                    JsLookup lookup = binaryOp.Operand1 as JsLookup;
                    if (lookup != null)
                    {
                        // it is. see if that variable is in the previous var statement
                        areAssignmentsInVar = varStatement.Contains(lookup.Name);
                    }
                }
                else if (binaryOp.OperatorToken == JsToken.Comma)
                {
                    // this is a comma operator, so we will return true only if both
                    // left and right operators are assignments to vars defined in the
                    // var statement
                    areAssignmentsInVar = AreAssignmentsInVar(binaryOp.Operand1 as JsBinaryOperator, varStatement)
                        && AreAssignmentsInVar(binaryOp.Operand2 as JsBinaryOperator, varStatement);
                }
            }

            return areAssignmentsInVar;
        }
Ejemplo n.º 27
0
        private void MeasureBinaryOperator(JsBinaryOperator node)
        {
            // depending on the operator, calculate the potential difference in length
            switch (node.OperatorToken)
            {
            case JsToken.Equal:
            case JsToken.NotEqual:
            case JsToken.StrictEqual:
            case JsToken.StrictNotEqual:
                // these operators can be turned into a logical not without any
                // delta in code size. == becomes !=, etc.
                break;

            case JsToken.LessThan:
            case JsToken.GreaterThan:
            // these operators would add another character when turnbed into a not.
            // for example, < becomes >=, etc
            //++m_delta;
            //break;

            case JsToken.LessThanEqual:
            case JsToken.GreaterThanEqual:
            // these operators would subtract another character when turnbed into a not.
            // for example, <= becomes >, etc
            //--m_delta;
            //break;

            case JsToken.Assign:
            case JsToken.PlusAssign:
            case JsToken.MinusAssign:
            case JsToken.MultiplyAssign:
            case JsToken.DivideAssign:
            case JsToken.ModuloAssign:
            case JsToken.BitwiseAndAssign:
            case JsToken.BitwiseOrAssign:
            case JsToken.BitwiseXorAssign:
            case JsToken.LeftShiftAssign:
            case JsToken.RightShiftAssign:
            case JsToken.UnsignedRightShiftAssign:
            case JsToken.BitwiseAnd:
            case JsToken.BitwiseOr:
            case JsToken.BitwiseXor:
            case JsToken.Divide:
            case JsToken.Multiply:
            case JsToken.Modulo:
            case JsToken.Minus:
            case JsToken.Plus:
            case JsToken.LeftShift:
            case JsToken.RightShift:
            case JsToken.UnsignedRightShift:
            case JsToken.In:
            case JsToken.InstanceOf:
                // these operators have no logical not, which means we need to wrap them in
                // a unary logical-not operator. And since they have a lower precedence than
                // the unary logical-not, they'll have to be wrapped in parens. So that means
                // logical-not'ing these guys adds three characters
                m_delta += 3;
                break;

            case JsToken.Comma:
                // to logical-not a comma-operator, we just need to logical-not the
                // right-hand side
                node.Operand2.Accept(this);
                break;

            case JsToken.LogicalAnd:
            case JsToken.LogicalOr:
                if (node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock))
                {
                    // if the parent is a block, then this is a simple expression statement:
                    // expr1 || expr2; or expr1 && expr2; If so, then the result isn't
                    // used anywhere and we're just using the || or && operator as a
                    // shorter if-statement. So we don't need to negate the right-hand
                    // side, just the left-hand side.
                    if (node.Operand1 != null)
                    {
                        node.Operand1.Accept(this);
                    }
                }
                else
                {
                    // the logical-not of a logical-and or logical-or operation is the
                    // other operation against the not of each operand. Since the opposite
                    // operator is the same length as this operator, then we just need
                    // to recurse both operands to find the true delta.
                    if (node.Operand1 != null)
                    {
                        node.Operand1.Accept(this);
                    }

                    if (node.Operand2 != null)
                    {
                        node.Operand2.Accept(this);
                    }
                }
                break;
            }
        }
        public override void Visit(JsIfNode node)
        {
            if (node != null)
            {
                if (m_parser.Settings.StripDebugStatements
                     && m_parser.Settings.IsModificationAllowed(JsTreeModifications.StripDebugStatements))
                {
                    if (node.TrueBlock != null && node.TrueBlock.IsDebuggerStatement)
                    {
                        node.TrueBlock = null;
                    }

                    if (node.FalseBlock != null && node.FalseBlock.IsDebuggerStatement)
                    {
                        node.FalseBlock = 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.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(JsTreeModifications.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.
                        JsConditional conditional;
                        var logicalNot = new JsLogicalNot(node.Condition, m_parser);
                        if (logicalNot.Measure() < 0)
                        {
                            // applying a logical-not makes the condition smaller -- reverse the branches
                            logicalNot.Apply();
                            conditional = new JsConditional(node.Context, m_parser)
                                {
                                    Condition = node.Condition,
                                    TrueExpression = node.FalseBlock[0],
                                    FalseExpression = node.TrueBlock[0]
                                };
                        }
                        else
                        {
                            // regular order
                            conditional = new JsConditional(node.Context, m_parser)
                                {
                                    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 JsLogicalNot(node.Condition, m_parser);
                        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 JsReturnNode;
                            if (trueReturn != null && trueReturn.Operand != null)
                            {
                                // it is -- see if the false-branch is also a return statement
                                var falseReturn = node.FalseBlock[0] as JsReturnNode;
                                if (falseReturn != null && falseReturn.Operand != null)
                                {
                                    // transform: if(cond)return expr1;else return expr2 to return cond?expr1:expr2
                                    var conditional = new JsConditional(null, m_parser)
                                        {
                                            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 JsReturnNode(node.Context, m_parser)
                                        {
                                            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(JsTreeModifications.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 JsLogicalNot(node.Condition, m_parser);
                        if (logicalNot.Measure() < 0)
                        {
                            // !a is smaller, so apply it and use the logical-or operator
                            logicalNot.Apply();
                            newOperator = JsToken.LogicalAnd;
                        }

                        var binaryOp = new JsBinaryOperator(node.Context, m_parser)
                            {
                                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(JsTreeModifications.IfConditionFalseToIfNotConditionTrue))
                    {
                        // logical-not the condition
                        // if(cond);else stmt ==> if(!cond)stmt
                        var logicalNot = new JsLogicalNot(node.Condition, m_parser);
                        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(JsTreeModifications.IfConditionCallToConditionAndCall))
                    {
                        // convert the if-node to an expression
                        IfConditionExpressionToExpression(node, node.TrueBlock[0]);
                    }
                }
                else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.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.IsConstant || node.Condition.IsDebuggerStatement;
                    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(JsTreeModifications.CombineNestedIfs))
                {
                    var nestedIf = node.TrueBlock[0] as JsIfNode;
                    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 JsBinaryOperator(null, m_parser)
                            {
                                Operand1 = node.Condition,
                                Operand2 = nestedIf.Condition,
                                OperatorToken = JsToken.LogicalAnd
                            };
                        node.TrueBlock = nestedIf.TrueBlock;
                    }
                }
            }
        }
 private static void RotateOpeator(JsBinaryOperator node, JsAstNodeList rightSide)
 {
     if (rightSide.Count == 0)
     {
         // the list is empty -- remove the node altogether
         node.Parent.ReplaceChild(node, null);
     }
     else if (rightSide.Count == 1)
     {
         // the list has only one item -- replace the node with the one item
         node.Parent.ReplaceChild(node, rightSide[0]);
     }
     else if (rightSide.Count == 2)
     {
         // there are only two items -- rotate the first to the left-hand side
         // and replace the right-hand side with the second item
         node.Operand1 = rightSide[0];
         node.Operand2 = rightSide[1];
     }
     else
     {
         // there will still be more than one left in the list after we peel off the
         // first one. rotate the first item to the left-hand side
         var temp = rightSide[0];
         rightSide.RemoveAt(0);
         node.Operand1 = temp;
     }
 }
        /// <summary>
        /// We have determined that our right-hand operand is another binary operator, and its
        /// left-hand operand is a constant that can be combined with our left-hand operand.
        /// Now we want to set the left-hand operand of that other operator to the newly-
        /// combined constant value, and then rotate it up -- replace our binary operator
        /// with this newly-modified binary operator, and then attempt to re-evaluate it.
        /// </summary>
        /// <param name="binaryOp">the binary operator that is our right-hand operand</param>
        /// <param name="newLiteral">the newly-combined literal</param>
        /// <param name="node"></param>
        private void RotateFromRight(JsBinaryOperator node, JsBinaryOperator binaryOp, JsConstantWrapper newLiteral)
        {
            // replace our node with the binary operator
            binaryOp.Operand1 = newLiteral;
            node.Parent.ReplaceChild(node, binaryOp);

            // and just for good measure.. revisit the node that's taking our place, since
            // we just changed a constant value. Assuming the other operand is a constant, too.
            JsConstantWrapper otherConstant = binaryOp.Operand2 as JsConstantWrapper;
            if (otherConstant != null)
            {
                EvalThisOperator(binaryOp, newLiteral, otherConstant);
            }
        }
 public void Visit(JsBinaryOperator node)
 {
     // not applicable; terminate
 }
        private void DoBinaryOperator(JsBinaryOperator 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 != JsPrimitiveType.Other)
                        {
                            var rightType = node.Operand2.FindPrimitiveType();
                            if (rightType != JsPrimitiveType.Other)
                            {
                                // both sides are known
                                if (leftType != rightType)
                                {
                                    // they are not the same type -- replace with a boolean and bail
                                    ReplaceNodeWithLiteral(
                                        node,
                                        new JsConstantWrapper(node.OperatorToken == JsToken.StrictEqual ? false : true, JsPrimitiveType.Boolean, node.Context, m_parser));
                                    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
                    JsConstantWrapper left = node.Operand1 as JsConstantWrapper;
                    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
                            JsConstantWrapper rightConstant = node.Operand2 as JsConstantWrapper;
                            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 JsCommaOperator)
                            {
                                // this is a collection of expression statements that we've joined together as
                                // an extended comma operator.
                                var list = node.Operand2 as JsAstNodeList;
                                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
                            JsConstantWrapper right = node.Operand2 as JsConstantWrapper;
                            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
                                JsBinaryOperator rightBinary = node.Operand2 as JsBinaryOperator;
                                if (rightBinary != null)
                                {
                                    JsConstantWrapper rightLeft = rightBinary.Operand1 as JsConstantWrapper;
                                    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
                                    {
                                        JsConstantWrapper rightRight = rightBinary.Operand2 as JsConstantWrapper;
                                        if (rightRight != null)
                                        {
                                            EvalFarToTheRight(node, left, rightRight, rightBinary);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // left is not a constantwrapper. See if the right is
                        JsConstantWrapper right = node.Operand2 as JsConstantWrapper;
                        if (right != null)
                        {
                            // the right is a constant. See if the the left is a binary operator...
                            JsBinaryOperator leftBinary = node.Operand1 as JsBinaryOperator;
                            if (leftBinary != null)
                            {
                                // ...with a constant on the right, and the operators can be combined
                                JsConstantWrapper leftRight = leftBinary.Operand2 as JsConstantWrapper;
                                if (leftRight != null)
                                {
                                    EvalToTheLeft(node, right, leftRight, leftBinary);
                                }
                                else
                                {
                                    JsConstantWrapper leftLeft = leftBinary.Operand1 as JsConstantWrapper;
                                    if (leftLeft != null)
                                    {
                                        EvalFarToTheLeft(node, right, leftLeft, leftBinary);
                                    }
                                }
                            }
                            else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.SimplifyStringToNumericConversion))
                            {
                                // see if it's a lookup and this is a minus operation and the constant is a zero
                                JsLookup lookup = node.Operand1 as JsLookup;
                                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 JsUnaryOperator(node.Context, m_parser)
                                        {
                                            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)
                    }
                }
            }
        }
        private void EvalThisOperator(JsBinaryOperator node, JsConstantWrapper left, JsConstantWrapper right)
        {
            // we can evaluate these operators if we know both operands are literal
            // number, boolean, string or null
            JsConstantWrapper newLiteral = null;
            switch (node.OperatorToken)
            {
                case JsToken.Multiply:
                    newLiteral = Multiply(left, right);
                    break;

                case JsToken.Divide:
                    newLiteral = Divide(left, right);
                    if (newLiteral != null && newLiteral.ToCode().Length > node.ToCode().Length)
                    {
                        // the result is bigger than the expression.
                        // eg: 1/3 is smaller than .333333333333333
                        // never mind.
                        newLiteral = null;
                    }
                    break;

                case JsToken.Modulo:
                    newLiteral = Modulo(left, right);
                    if (newLiteral != null && newLiteral.ToCode().Length > node.ToCode().Length)
                    {
                        // the result is bigger than the expression.
                        // eg: 46.5%6.3 is smaller than 2.4000000000000012
                        // never mind.
                        newLiteral = null;
                    }
                    break;

                case JsToken.Plus:
                    newLiteral = Plus(left, right);
                    break;

                case JsToken.Minus:
                    newLiteral = Minus(left, right);
                    break;

                case JsToken.LeftShift:
                    newLiteral = LeftShift(left, right);
                    break;

                case JsToken.RightShift:
                    newLiteral = RightShift(left, right);
                    break;

                case JsToken.UnsignedRightShift:
                    newLiteral = UnsignedRightShift(left, right);
                    break;

                case JsToken.LessThan:
                    newLiteral = LessThan(left, right);
                    break;

                case JsToken.LessThanEqual:
                    newLiteral = LessThanOrEqual(left, right);
                    break;

                case JsToken.GreaterThan:
                    newLiteral = GreaterThan(left, right);
                    break;

                case JsToken.GreaterThanEqual:
                    newLiteral = GreaterThanOrEqual(left, right);
                    break;

                case JsToken.Equal:
                    newLiteral = Equal(left, right);
                    break;

                case JsToken.NotEqual:
                    newLiteral = NotEqual(left, right);
                    break;

                case JsToken.StrictEqual:
                    newLiteral = StrictEqual(left, right);
                    break;

                case JsToken.StrictNotEqual:
                    newLiteral = StrictNotEqual(left, right);
                    break;

                case JsToken.BitwiseAnd:
                    newLiteral = BitwiseAnd(left, right);
                    break;

                case JsToken.BitwiseOr:
                    newLiteral = BitwiseOr(left, right);
                    break;

                case JsToken.BitwiseXor:
                    newLiteral = BitwiseXor(left, right);
                    break;

                case JsToken.LogicalAnd:
                    newLiteral = LogicalAnd(left, right);
                    break;

                case JsToken.LogicalOr:
                    newLiteral = LogicalOr(left, right);
                    break;

                default:
                    // an operator we don't want to evaluate
                    break;
            }

            // if we can combine them...
            if (newLiteral != null)
            {
                // first we want to check if the new combination is a string literal, and if so, whether
                // it's now the sole parameter of a member-bracket call operator. If so, instead of replacing our
                // binary operation with the new constant, we'll replace the entire call with a member-dot
                // expression
                if (!ReplaceMemberBracketWithDot(node, newLiteral))
                {
                    ReplaceNodeWithLiteral(node, newLiteral);
                }
            }
        }
 private static void ConvertAssignmentsToVarDecls(JsBinaryOperator binaryOp, List<JsVariableDeclaration> varDecls, JsParser parser)
 {
     // we've already checked that the tree only contains simple assignments separate by commas,
     // but just in case we'll check for null anyway
     if (binaryOp != null)
     {
         if (binaryOp.OperatorToken == JsToken.Assign)
         {
             // we've already cleared this as a simple lookup, but run the check just to be sure
             JsLookup lookup = binaryOp.Operand1 as JsLookup;
             if (lookup != null)
             {
                 var varDecl = new JsVariableDeclaration(binaryOp.Context.Clone(), parser)
                     {
                         Identifier = lookup.Name,
                         NameContext = lookup.Context.Clone(),
                         AssignContext = binaryOp.OperatorContext,
                         Initializer = binaryOp.Operand2,
                         VariableField = lookup.VariableField
                     };
                 varDecl.VariableField.Declarations.Add(varDecl);
                 varDecls.Add(varDecl);
             }
         }
         else if (binaryOp.OperatorToken == JsToken.Comma)
         {
             // recurse both operands
             ConvertAssignmentsToVarDecls(binaryOp.Operand1 as JsBinaryOperator, varDecls, parser);
             ConvertAssignmentsToVarDecls(binaryOp.Operand2 as JsBinaryOperator, varDecls, parser);
         }
         // shouldn't ever be anything but these two operators
     }
 }
        public void Visit(JsBinaryOperator node)
        {
            if (node != null)
            {
                if (node.Operand1 != null)
                {
                    node.Operand1.Accept(this);
                }

                if (node.Operand2 != null)
                {
                    node.Operand2.Accept(this);
                }

                node.Index = NextOrderIndex;
            }
        }
        private void EvalToTheLeft(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator leftOperator)
        {
            if (leftOperator.OperatorToken == JsToken.Plus && node.OperatorToken == JsToken.Plus)
            {
                // plus-plus
                // the other operation goes first, so if the other constant is a string, then we know that
                // operation will do a string concatenation, which will force our operation to be a string
                // concatenation. If the other constant is not a string, then we won't know until runtime and
                // we can't combine them.
                if (otherConstant.IsStringLiteral)
                {
                    // the other constant is a string -- so we can do the string concat and combine them
                    JsConstantWrapper newLiteral = StringConcat(otherConstant, thisConstant);
                    if (newLiteral != null)
                    {
                        RotateFromLeft(node, leftOperator, newLiteral);
                    }
                }
            }
            else if (leftOperator.OperatorToken == JsToken.Minus)
            {
                if (node.OperatorToken == JsToken.Plus)
                {
                    // minus-plus
                    // the minus operator goes first and will always convert to number.
                    // if our constant is not a string, then it will be a numeric addition and we can combine them.
                    // if our constant is a string, then we'll end up doing a string concat, so we can't combine
                    if (!thisConstant.IsStringLiteral)
                    {
                        // two numeric operators. a-n1+n2 is the same as a-(n1-n2)
                        JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            // a-(-n) is numerically equivalent as a+n -- and takes fewer characters to represent.
                            // BUT we can't do that because that might change a numeric operation (the original minus)
                            // to a string concatenation if the unknown operand turns out to be a string!

                            RotateFromLeft(node, leftOperator, newLiteral);
                        }
                        else
                        {
                            // if the left-left is a constant, then we can try combining with it
                            JsConstantWrapper leftLeft = leftOperator.Operand1 as JsConstantWrapper;
                            if (leftLeft != null)
                            {
                                EvalFarToTheLeft(node, thisConstant, leftLeft, leftOperator);
                            }
                        }
                    }
                }
                else if (node.OperatorToken == JsToken.Minus)
                {
                    // minus-minus. Both operations are numeric.
                    // (a-n1)-n2 => a-(n1+n2), so we can add the two constants and subtract from
                    // the left-hand non-constant.
                    JsConstantWrapper newLiteral = NumericAddition(otherConstant, thisConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        // make it the new right-hand literal for the left-hand operator
                        // and make the left-hand operator replace our operator
                        RotateFromLeft(node, leftOperator, newLiteral);
                    }
                    else
                    {
                        // if the left-left is a constant, then we can try combining with it
                        JsConstantWrapper leftLeft = leftOperator.Operand1 as JsConstantWrapper;
                        if (leftLeft != null)
                        {
                            EvalFarToTheLeft(node, thisConstant, leftLeft, leftOperator);
                        }
                    }
                }
            }
            else if (leftOperator.OperatorToken == node.OperatorToken
                && (node.OperatorToken == JsToken.Multiply || node.OperatorToken == JsToken.Divide))
            {
                // either multiply-multiply or divide-divide
                // either way, we use the other operand and the product of the two constants.
                // if the product blows up to an infinte value, then don't combine them because that
                // could change the way the program goes at runtime, depending on the unknown value.
                JsConstantWrapper newLiteral = Multiply(otherConstant, thisConstant);
                if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, newLiteral))
                {
                    RotateFromLeft(node, leftOperator, newLiteral);
                }
            }
            else if ((leftOperator.OperatorToken == JsToken.Multiply && node.OperatorToken == JsToken.Divide)
                || (leftOperator.OperatorToken == JsToken.Divide && node.OperatorToken == JsToken.Multiply))
            {
                if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions))
                {
                    // get the two division operators
                    JsConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    JsConstantWrapper thisOverOther = Divide(thisConstant, otherConstant);

                    // get the lengths
                    int otherOverThisLength = otherOverThis != null ? otherOverThis.ToCode().Length : int.MaxValue;
                    int thisOverOtherLength = thisOverOther != null ? thisOverOther.ToCode().Length : int.MaxValue;

                    // we'll want to use whichever one is shorter, and whichever one does NOT involve an overflow
                    // or possible underflow
                    if (otherOverThis != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, otherOverThis)
                        && (thisOverOther == null || otherOverThisLength < thisOverOtherLength))
                    {
                        // but only if it's smaller than the original expression
                        if (otherOverThisLength <= otherConstant.ToCode().Length + thisConstant.ToCode().Length + 1)
                        {
                            // same operator
                            RotateFromLeft(node, leftOperator, otherOverThis);
                        }
                    }
                    else if (thisOverOther != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, thisOverOther))
                    {
                        // but only if it's smaller than the original expression
                        if (thisOverOtherLength <= otherConstant.ToCode().Length + thisConstant.ToCode().Length + 1)
                        {
                            // opposite operator
                            leftOperator.OperatorToken = leftOperator.OperatorToken == JsToken.Multiply ? JsToken.Divide : JsToken.Multiply;
                            RotateFromLeft(node, leftOperator, thisOverOther);
                        }
                    }
                }
            }
            else if (node.OperatorToken == leftOperator.OperatorToken
                && (node.OperatorToken == JsToken.BitwiseAnd || node.OperatorToken == JsToken.BitwiseOr || node.OperatorToken == JsToken.BitwiseXor))
            {
                // identical bitwise operators can be combined
                JsConstantWrapper newLiteral = null;
                switch (node.OperatorToken)
                {
                    case JsToken.BitwiseAnd:
                        newLiteral = BitwiseAnd(otherConstant, thisConstant);
                        break;

                    case JsToken.BitwiseOr:
                        newLiteral = BitwiseOr(otherConstant, thisConstant);
                        break;

                    case JsToken.BitwiseXor:
                        newLiteral = BitwiseXor(otherConstant, thisConstant);
                        break;
                }
                if (newLiteral != null)
                {
                    RotateFromLeft(node, leftOperator, newLiteral);
                }
            }
        }
Ejemplo n.º 37
0
 public void Visit(JsBinaryOperator node)
 {
     // not applicable; terminate
 }
        private void EvalToTheRight(JsBinaryOperator node, JsConstantWrapper thisConstant, JsConstantWrapper otherConstant, JsBinaryOperator rightOperator)
        {
            if (node.OperatorToken == JsToken.Plus)
            {
                if (rightOperator.OperatorToken == JsToken.Plus && otherConstant.IsStringLiteral)
                {
                    // plus-plus, and the other constant is a string. So the right operator will be a string-concat
                    // that generates a string. And since this is a plus-operator, then this operator will be a string-
                    // concat as well. So we can just combine the strings now and replace our node with the right-hand
                    // operation
                    JsConstantWrapper newLiteral = StringConcat(thisConstant, otherConstant);
                    if (newLiteral != null)
                    {
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Minus && !thisConstant.IsStringLiteral)
                {
                    // plus-minus. Now, the minus operation happens first, and it will perform a numeric
                    // operation. The plus is NOT string, so that means it will also be a numeric operation
                    // and we can combine the operators numericly.
                    JsConstantWrapper newLiteral = NumericAddition(thisConstant, otherConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                    else
                    {
                        JsConstantWrapper rightRight = rightOperator.Operand2 as JsConstantWrapper;
                        if (rightRight != null)
                        {
                            EvalFarToTheRight(node, thisConstant, rightRight, rightOperator);
                        }
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Minus && rightOperator.OperatorToken == JsToken.Minus)
            {
                // minus-minus
                // both operations are numeric, so we can combine the constant operands. However, we
                // can't combine them into a plus, so make sure we do the minus in the opposite direction
                JsConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                if (newLiteral != null && NoOverflow(newLiteral))
                {
                    rightOperator.SwapOperands();
                    RotateFromLeft(node, rightOperator, newLiteral);
                }
                else
                {
                    JsConstantWrapper rightRight = rightOperator.Operand2 as JsConstantWrapper;
                    if (rightRight != null)
                    {
                        EvalFarToTheRight(node, thisConstant, rightRight, rightOperator);
                    }
                }
            }
            else if (node.OperatorToken == JsToken.Multiply
                && (rightOperator.OperatorToken == JsToken.Multiply || rightOperator.OperatorToken == JsToken.Divide))
            {
                // multiply-divide or multiply-multiply
                // multiply the operands and use the right-hand operator
                JsConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                {
                    RotateFromRight(node, rightOperator, newLiteral);
                }
            }
            else if (node.OperatorToken == JsToken.Divide)
            {
                if (rightOperator.OperatorToken == JsToken.Multiply)
                {
                    // divide-multiply
                    JsConstantWrapper newLiteral = Divide(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral)
                        && newLiteral.ToCode().Length < thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                    {
                        // flip the operator: multiply becomes divide; devide becomes multiply
                        rightOperator.OperatorToken = JsToken.Divide;

                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JsToken.Divide)
                {
                    // divide-divide
                    // get constants for left/right and for right/left
                    JsConstantWrapper leftOverRight = Divide(thisConstant, otherConstant);
                    JsConstantWrapper rightOverLeft = Divide(otherConstant, thisConstant);

                    // get the lengths of the resulting code
                    int leftOverRightLength = leftOverRight != null ? leftOverRight.ToCode().Length : int.MaxValue;
                    int rightOverLeftLength = rightOverLeft != null ? rightOverLeft.ToCode().Length : int.MaxValue;

                    // try whichever is smaller
                    if (leftOverRight != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, leftOverRight)
                        && (rightOverLeft == null || leftOverRightLength < rightOverLeftLength))
                    {
                        // use left-over-right.
                        // but only if the resulting value is smaller than the original expression
                        if (leftOverRightLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // We don't need to swap the operands, but we do need to switch the operator
                            rightOperator.OperatorToken = JsToken.Multiply;
                            RotateFromRight(node, rightOperator, leftOverRight);
                        }
                    }
                    else if (rightOverLeft != null && NoMultiplicativeOverOrUnderFlow(otherConstant, thisConstant, rightOverLeft))
                    {
                        // but only if the resulting value is smaller than the original expression
                        if (rightOverLeftLength <= thisConstant.ToCode().Length + otherConstant.ToCode().Length + 1)
                        {
                            // use right-over-left. Keep the operator, but swap the operands
                            rightOperator.SwapOperands();
                            RotateFromLeft(node, rightOperator, rightOverLeft);
                        }
                    }
                }
            }
        }
 public void Visit(JsBinaryOperator node)
 {
     // invalid! ignore
     IsValid = false;
 }
        /// <summary>
        /// If the new literal is a string literal, then we need to check to see if our
        /// parent is a CallNode. If it is, and if the string literal can be an identifier,
        /// we'll replace it with a Member-Dot operation.
        /// </summary>
        /// <param name="newLiteral">newLiteral we intend to replace this binaryop node with</param>
        /// <returns>true if we replaced the parent callnode with a member-dot operation</returns>
        /// <param name="node"></param>
        private bool ReplaceMemberBracketWithDot(JsBinaryOperator node, JsConstantWrapper newLiteral)
        {
            if (newLiteral.IsStringLiteral)
            {
                // see if this newly-combined string is the sole argument to a
                // call-brackets node. If it is and the combined string is a valid
                // identifier (and not a keyword), then we can replace the call
                // with a member operator.
                // remember that the parent of the argument won't be the call node -- it
                // will be the ast node list representing the arguments, whose parent will
                // be the node list.
                JsCallNode parentCall = (node.Parent is JsAstNodeList ? node.Parent.Parent as JsCallNode : null);
                if (parentCall != null && parentCall.InBrackets)
                {
                    // get the newly-combined string
                    string combinedString = newLiteral.ToString();

                    // see if this new string is the target of a replacement operation
                    string newName;
                    if (m_parser.Settings.HasRenamePairs && m_parser.Settings.ManualRenamesProperties
                        && m_parser.Settings.IsModificationAllowed(JsTreeModifications.PropertyRenaming)
                        && !string.IsNullOrEmpty(newName = m_parser.Settings.GetNewName(combinedString)))
                    {
                        // yes, it is. Now see if the new name is safe to be converted to a dot-operation.
                        if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.BracketMemberToDotMember)
                            && JsScanner.IsSafeIdentifier(newName)
                            && !JsScanner.IsKeyword(newName, parentCall.EnclosingScope.UseStrict))
                        {
                            // we want to replace the call with operator with a new member dot operation, and
                            // since we won't be analyzing it (we're past the analyze phase, we're going to need
                            // to use the new string value
                            JsMember replacementMember = new JsMember(parentCall.Context, m_parser)
                                {
                                    Root = parentCall.Function,
                                    Name = newName,
                                    NameContext = parentCall.Arguments[0].Context
                                };
                            parentCall.Parent.ReplaceChild(parentCall, replacementMember);
                            return true;
                        }
                        else
                        {
                            // nope, can't be changed to a dot-operator for whatever reason.
                            // just replace the value on this new literal. The old operation will
                            // get replaced with this new literal
                            newLiteral.Value = newName;

                            // and make sure it's type is string
                            newLiteral.PrimitiveType = JsPrimitiveType.String;
                        }
                    }
                    else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.BracketMemberToDotMember))
                    {
                        // our parent is a call-bracket -- now we just need to see if the newly-combined
                        // string can be an identifier
                        if (JsScanner.IsSafeIdentifier(combinedString) && !JsScanner.IsKeyword(combinedString, parentCall.EnclosingScope.UseStrict))
                        {
                            // yes -- replace the parent call with a new member node using the newly-combined string
                            JsMember replacementMember = new JsMember(parentCall.Context, m_parser)
                                {
                                    Root = parentCall.Function,
                                    Name = combinedString,
                                    NameContext = parentCall.Arguments[0].Context
                                };
                            parentCall.Parent.ReplaceChild(parentCall, replacementMember);
                            return true;
                        }
                    }
                }
            }
            return false;
        }