コード例 #1
0
        public override void Visit(BinaryOperator 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);
            }
        }
コード例 #2
0
        private void DoBinaryOperator(BinaryOperator node)
        {
            if (m_parser.Settings.EvalLiteralExpressions)
            {
                // if this is an assign operator, an in, or an instanceof, then we won't
                // try to evaluate it
                if (!node.IsAssign && node.OperatorToken != JSToken.In && node.OperatorToken != JSToken.InstanceOf)
                {
                    if (node.OperatorToken == JSToken.StrictEqual || node.OperatorToken == JSToken.StrictNotEqual)
                    {
                        // the operator is a strict equality (or not-equal).
                        // check the primitive types of the two operands -- if they are known but not the same, we can
                        // shortcut the whole process by just replacing this node with a boolean literal.
                        var leftType = node.Operand1.FindPrimitiveType();
                        if (leftType != PrimitiveType.Other)
                        {
                            var rightType = node.Operand2.FindPrimitiveType();
                            if (rightType != PrimitiveType.Other)
                            {
                                // both sides are known
                                if (leftType != rightType)
                                {
                                    // they are not the same type -- replace with a boolean and bail
                                    ReplaceNodeWithLiteral(
                                        node, 
                                        new ConstantWrapper(node.OperatorToken == JSToken.StrictEqual ? false : true, PrimitiveType.Boolean, node.Context, 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
                    ConstantWrapper left = node.Operand1 as ConstantWrapper;
                    if (left != null)
                    {
                        if (node.OperatorToken == JSToken.Comma)
                        {
                            // the comma operator evaluates the left, then evaluates the right and returns it.
                            // but if the left is a literal, evaluating it doesn't DO anything, so we can replace the
                            // entire operation with the right-hand operand
                            ConstantWrapper rightConstant = node.Operand2 as ConstantWrapper;
                            if (rightConstant != null)
                            {
                                // we'll replace the operator with the right-hand operand, BUT it's a constant, too.
                                // first check to see if replacing this node with a constant will result in creating
                                // a member-bracket operator that can be turned into a member-dot. If it is, then that
                                // method will handle the replacement. But if it doesn't, then we should just replace
                                // the comma with the right-hand operand.
                                if (!ReplaceMemberBracketWithDot(node, rightConstant))
                                {
                                    ReplaceNodeWithLiteral(node, rightConstant);
                                }
                            }
                            else if (node is CommaOperator)
                            {
                                // this is a collection of expression statements that we've joined together as
                                // an extended comma operator. 
                                var list = node.Operand2 as AstNodeList;
                                if (list == null)
                                {
                                    // not a list, just a single item, so we can just
                                    // replace this entire node with the one element
                                    ReplaceNodeCheckParens(node, node.Operand2);
                                }
                                else if (list.Count == 1)
                                {
                                    // If the list has a single element, then we can just
                                    // replace this entire node with the one element
                                    ReplaceNodeCheckParens(node, list[0]);
                                }
                                else if (list.Count == 0)
                                {
                                    // the recursion ended up emptying the list, so we can just delete
                                    // this node altogether
                                    ReplaceNodeCheckParens(node, null);
                                }
                                else
                                {
                                    // more than one item in the list
                                    // move the first item from the list to the left-hand side
                                    var firstItem = list[0];
                                    list.RemoveAt(0);
                                    node.Operand1 = firstItem;

                                    // if there's only one item left in the list, we can get rid of the
                                    // extra list node and make it just the remaining node
                                    if (list.Count == 1)
                                    {
                                        firstItem = list[0];
                                        list.RemoveAt(0);
                                        node.Operand2 = firstItem;
                                    }
                                }
                            }
                            else
                            {
                                // replace the comma operator with the right-hand operand
                                ReplaceNodeCheckParens(node, node.Operand2);
                            }
                        }
                        else
                        {
                            // see if the right operand is a literal number, boolean, string, or null
                            ConstantWrapper right = node.Operand2 as ConstantWrapper;
                            if (right != null)
                            {
                                // then they are both constants and we can evaluate the operation
                                EvalThisOperator(node, left, right);
                            }
                            else
                            {
                                // see if the right is a binary operator that can be combined with ours
                                BinaryOperator rightBinary = node.Operand2 as BinaryOperator;
                                if (rightBinary != null)
                                {
                                    ConstantWrapper rightLeft = rightBinary.Operand1 as ConstantWrapper;
                                    if (rightLeft != null)
                                    {
                                        // eval our left and the right-hand binary's left and put the combined operation as
                                        // the child of the right-hand binary
                                        EvalToTheRight(node, left, rightLeft, rightBinary);
                                    }
                                    else
                                    {
                                        ConstantWrapper rightRight = rightBinary.Operand2 as ConstantWrapper;
                                        if (rightRight != null)
                                        {
                                            EvalFarToTheRight(node, left, rightRight, rightBinary);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // left is not a constantwrapper. See if the right is
                        ConstantWrapper right = node.Operand2 as ConstantWrapper;
                        if (right != null)
                        {
                            // the right is a constant. See if the the left is a binary operator...
                            BinaryOperator leftBinary = node.Operand1 as BinaryOperator;
                            if (leftBinary != null)
                            {
                                // ...with a constant on the right, and the operators can be combined
                                ConstantWrapper leftRight = leftBinary.Operand2 as ConstantWrapper;
                                if (leftRight != null)
                                {
                                    EvalToTheLeft(node, right, leftRight, leftBinary);
                                }
                                else
                                {
                                    ConstantWrapper leftLeft = leftBinary.Operand1 as ConstantWrapper;
                                    if (leftLeft != null)
                                    {
                                        EvalFarToTheLeft(node, right, leftLeft, leftBinary);
                                    }
                                }
                            }
                            else if (m_parser.Settings.IsModificationAllowed(TreeModifications.SimplifyStringToNumericConversion))
                            {
                                // see if it's a lookup and this is a minus operation and the constant is a zero
                                Lookup lookup = node.Operand1 as Lookup;
                                if (lookup != null && node.OperatorToken == JSToken.Minus && right.IsIntegerLiteral && right.ToNumber() == 0)
                                {
                                    // okay, so we have "lookup - 0"
                                    // this is done frequently to force a value to be numeric. 
                                    // There is an easier way: apply the unary + operator to it. 
                                    var unary = new UnaryOperator(node.Context, 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)
                    }
                }
            }
        }
コード例 #3
0
        public override void Visit(IfNode node)
        {
            if (node != null)
            {
                if (m_parser.Settings.StripDebugStatements
                     && m_parser.Settings.IsModificationAllowed(TreeModifications.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(TreeModifications.IfExpressionsToExpression))
                    {
                        // if this statement has both true and false blocks, and they are both expressions,
                        // then we can simplify this to a conditional expression.
                        // because the blocks are expressions, we know they only have ONE statement in them,
                        // so we can just dereference them directly.
                        Conditional conditional;
                        var logicalNot = new LogicalNot(node.Condition, m_parser);
                        if (logicalNot.Measure() < 0)
                        {
                            // applying a logical-not makes the condition smaller -- reverse the branches
                            logicalNot.Apply();
                            conditional = new Conditional(node.Context, m_parser)
                                {
                                    Condition = node.Condition,
                                    TrueExpression = node.FalseBlock[0],
                                    FalseExpression = node.TrueBlock[0]
                                };
                        }
                        else
                        {
                            // regular order
                            conditional = new Conditional(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 LogicalNot(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 ReturnNode;
                            if (trueReturn != null && trueReturn.Operand != null)
                            {
                                // it is -- see if the false-branch is also a return statement
                                var falseReturn = node.FalseBlock[0] as ReturnNode;
                                if (falseReturn != null && falseReturn.Operand != null)
                                {
                                    // transform: if(cond)return expr1;else return expr2 to return cond?expr1:expr2
                                    var conditional = new Conditional(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 ReturnNode(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(TreeModifications.IfConditionCallToConditionAndCall))
                    {
                        // if (cond); else expr ==> cond || expr
                        // but first -- which operator to use? if(a);else b --> a||b, and if(!a);else b --> a&&b
                        // so determine which one is smaller: a or !a
                        // assume we'll use the logical-or, since that doesn't require changing the condition
                        var newOperator = JSToken.LogicalOr;
                        var logicalNot = new LogicalNot(node.Condition, m_parser);
                        if (logicalNot.Measure() < 0)
                        {
                            // !a is smaller, so apply it and use the logical-or operator
                            logicalNot.Apply();
                            newOperator = JSToken.LogicalAnd;
                        }

                        var binaryOp = new BinaryOperator(node.Context, 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(TreeModifications.IfConditionFalseToIfNotConditionTrue))
                    {
                        // logical-not the condition
                        // if(cond);else stmt ==> if(!cond)stmt
                        var logicalNot = new LogicalNot(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(TreeModifications.IfConditionCallToConditionAndCall))
                    {
                        // convert the if-node to an expression
                        IfConditionExpressionToExpression(node, node.TrueBlock[0]);
                    }
                }
                else if (m_parser.Settings.IsModificationAllowed(TreeModifications.IfEmptyToExpression))
                {
                    // NEITHER branches have anything now!

                    // as long as the condition doesn't
                    // contain calls or assignments, we should be able to completely delete
                    // the statement altogether rather than changing it to an expression
                    // statement on the condition.
                    // but how do we KNOW there are no side-effects?
                    // if the condition is a constant or operations on constants, delete it.
                    // or if the condition itself is a debugger statement -- a call, lookup, or member.
                    var remove = node.Condition.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(TreeModifications.CombineNestedIfs))
                {
                    var nestedIf = node.TrueBlock[0] as IfNode;
                    if (nestedIf != null && nestedIf.FalseBlock == null)
                    {
                        // we have nested if-blocks.
                        // transform if(cond1)if(cond2){...} to if(cond1&&cond2){...}
                        // change the first if-statement's condition to be cond1&&cond2
                        // move the nested if-statement's true block to the outer if-statement
                        node.Condition = new BinaryOperator(null, m_parser)
                            {
                                Operand1 = node.Condition,
                                Operand2 = nestedIf.Condition,
                                OperatorToken = JSToken.LogicalAnd
                            };
                        node.TrueBlock = nestedIf.TrueBlock;
                    }
                }
            }
        }
コード例 #4
0
        public void Visit(BinaryOperator node)
        {
            if (node != null)
            {
                if (node.Operand1 != null)
                {
                    node.Operand1.Accept(this);
                }

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

                node.Index = NextOrderIndex;
            }
        }
コード例 #5
0
        private void EvalFarToTheLeft(BinaryOperator node, ConstantWrapper thisConstant, ConstantWrapper otherConstant, BinaryOperator 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 != PrimitiveType.String && thisConstant.PrimitiveType != PrimitiveType.Other)
                    {
                        ConstantWrapper newLiteral = NumericAddition(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            RotateFromRight(node, leftOperator, newLiteral);
                        }
                    }
                }
                else if (node.OperatorToken == JSToken.Minus)
                {
                    // minus-minus
                    ConstantWrapper 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)
                {
                    ConstantWrapper 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
                    ConstantWrapper 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
                    ConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    ConstantWrapper 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);
                        }
                    }
                }
            }
        }
コード例 #6
0
        private void EvalFarToTheRight(BinaryOperator node, ConstantWrapper thisConstant, ConstantWrapper otherConstant, BinaryOperator rightOperator)
        {
            if (rightOperator.OperatorToken == JSToken.Minus)
            {
                if (node.OperatorToken == JSToken.Plus)
                {
                    // plus-minus
                    // our constant cannot be a string, though
                    if (!thisConstant.IsStringLiteral)
                    {
                        ConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                        if (newLiteral != null && NoOverflow(newLiteral))
                        {
                            RotateFromLeft(node, rightOperator, newLiteral);
                        }
                    }
                }
                else if (node.OperatorToken == JSToken.Minus)
                {
                    // minus-minus
                    ConstantWrapper 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
                    ConstantWrapper newLiteral = Multiply(thisConstant, otherConstant);
                    if (newLiteral != null && NoMultiplicativeOverOrUnderFlow(thisConstant, otherConstant, newLiteral))
                    {
                        RotateFromLeft(node, rightOperator, newLiteral);
                    }
                }
                else if (rightOperator.OperatorToken == JSToken.Divide)
                {
                    // mult-divide
                    ConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    ConstantWrapper 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
                    ConstantWrapper 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
                    ConstantWrapper 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);
                    }
                }
            }
        }
コード例 #7
0
 public void Visit(BinaryOperator node)
 {
     // not applicable; terminate
 }
コード例 #8
0
        private void EvalToTheLeft(BinaryOperator node, ConstantWrapper thisConstant, ConstantWrapper otherConstant, BinaryOperator 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
                    ConstantWrapper 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)
                        ConstantWrapper 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
                            ConstantWrapper leftLeft = leftOperator.Operand1 as ConstantWrapper;
                            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. 
                    ConstantWrapper 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
                        ConstantWrapper leftLeft = leftOperator.Operand1 as ConstantWrapper;
                        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.
                ConstantWrapper 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(TreeModifications.EvaluateNumericExpressions))
                {
                    // get the two division operators
                    ConstantWrapper otherOverThis = Divide(otherConstant, thisConstant);
                    ConstantWrapper 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
                ConstantWrapper 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);
                }
            }
        }
コード例 #9
0
 private static void ConvertAssignmentsToVarDecls(BinaryOperator binaryOp, List<VariableDeclaration> 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
             Lookup lookup = binaryOp.Operand1 as Lookup;
             if (lookup != null)
             {
                 var varDecl = new VariableDeclaration(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 BinaryOperator, varDecls, parser);
             ConvertAssignmentsToVarDecls(binaryOp.Operand2 as BinaryOperator, varDecls, parser);
         }
         // shouldn't ever be anything but these two operators
     }
 }
コード例 #10
0
        public override void Visit(BinaryOperator 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(TreeModifications.SimplifyStringToNumericConversion))
                {
                    Lookup lookup = node.Operand1 as Lookup;
                    if (lookup != null)
                    {
                        ConstantWrapper right = node.Operand2 as ConstantWrapper;
                        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 UnaryOperator(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(TreeModifications.ReduceStrictOperatorIfTypesAreSame))
                {
                    PrimitiveType leftType = node.Operand1.FindPrimitiveType();
                    if (leftType != PrimitiveType.Other)
                    {
                        PrimitiveType 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 != PrimitiveType.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 ConstantWrapper(node.OperatorToken == JSToken.StrictNotEqual, PrimitiveType.Boolean, node.Context, m_parser));

                            // because we are essentially removing the node from the AST, be sure to detach any references
                            DetachReferences.Apply(node);
                        }
                    }
                }
                else if (node.IsAssign)
                {
                    var lookup = node.Operand1 as Lookup;
                    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 == FieldType.UndefinedGlobal)
                            {
                                // strict mode cannot assign to undefined fields
                                node.Operand1.Context.HandleError(JSError.StrictModeUndefinedVariable, true);
                            }
                            else if(lookup.VariableField.FieldType == FieldType.Arguments
                                || (lookup.VariableField.FieldType == FieldType.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 Block || (node.Parent is CommaOperator && node.Parent.Parent is Block))
                    && (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 LogicalNot(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;
                    }
                }
            }
        }
コード例 #11
0
        private static bool AreAssignmentsInVar(BinaryOperator binaryOp, Var 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
                    Lookup lookup = binaryOp.Operand1 as Lookup;
                    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 BinaryOperator, varStatement)
                        && AreAssignmentsInVar(binaryOp.Operand2 as BinaryOperator, varStatement);
                }
            }

            return areAssignmentsInVar;
        }
コード例 #12
0
        private void IfConditionExpressionToExpression(IfNode ifNode, AstNode 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 LogicalNot(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 BinaryOperator(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);
        }
コード例 #13
0
        private void CombineTwoExpressions(Block node, int ndx)
        {
            var prevBinary = node[ndx - 1] as BinaryOperator;
            var curBinary = node[ndx] as BinaryOperator;
            Lookup lookup;
            if (prevBinary != null
                && curBinary != null
                && prevBinary.IsAssign
                && curBinary.IsAssign
                && curBinary.OperatorToken != JSToken.Assign
                && (lookup = curBinary.Operand1 as Lookup) != null
                && prevBinary.Operand1.IsEquivalentTo(curBinary.Operand1))
            {
                if (prevBinary.OperatorToken == JSToken.Assign)
                {
                    // transform: lookup=expr1;lookup[OP]=expr2;  ==>  lookup=expr1[OP]expr2
                    var binOp = new BinaryOperator(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 = CommaOperator.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 = CommaOperator.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;
            }
        }
コード例 #14
0
        private void EvalThisOperator(BinaryOperator node, ConstantWrapper left, ConstantWrapper right)
        {
            // we can evaluate these operators if we know both operands are literal
            // number, boolean, string or null
            ConstantWrapper 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);
                }
            }
        }
コード例 #15
0
        private void MeasureBinaryOperator(BinaryOperator 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 Block || (node.Parent is CommaOperator && node.Parent.Parent is Block))
                    {
                        // 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;
            }
        }
コード例 #16
0
        /// <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>
        private void RotateFromRight(BinaryOperator node, BinaryOperator binaryOp, ConstantWrapper 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.
            ConstantWrapper otherConstant = binaryOp.Operand2 as ConstantWrapper;
            if (otherConstant != null)
            {
                EvalThisOperator(binaryOp, newLiteral, otherConstant);
            }
        }
コード例 #17
0
        private void ConvertBinaryOperator(BinaryOperator 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 Block || (node.Parent is CommaOperator && node.Parent.Parent is Block))
                    {
                        // 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;
            }
        }
コード例 #18
0
        /// <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>
        private bool ReplaceMemberBracketWithDot(BinaryOperator node, ConstantWrapper 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. 
                CallNode parentCall = (node.Parent is AstNodeList ? node.Parent.Parent as CallNode : 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(TreeModifications.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(TreeModifications.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
                            Member replacementMember = new Member(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 = PrimitiveType.String;
                        }
                    }
                    else if (m_parser.Settings.IsModificationAllowed(TreeModifications.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
                            Member replacementMember = new Member(parentCall.Context, m_parser)
                                {
                                    Root = parentCall.Function,
                                    Name = combinedString,
                                    NameContext = parentCall.Arguments[0].Context
                                };
                            parentCall.Parent.ReplaceChild(parentCall, replacementMember);
                            return true;
                        }
                    }
                }
            }
            return false;
        }
コード例 #19
0
 public override void Visit(BinaryOperator node)
 {
     if (node != null)
     {
         if (m_measure)
         {
             // measure
             MeasureBinaryOperator(node);
         }
         else
         {
             // convert
             ConvertBinaryOperator(node);
         }
     }
 }
コード例 #20
0
        private void EvalToTheRight(BinaryOperator node, ConstantWrapper thisConstant, ConstantWrapper otherConstant, BinaryOperator 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
                    ConstantWrapper 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. 
                    ConstantWrapper newLiteral = NumericAddition(thisConstant, otherConstant);
                    if (newLiteral != null && NoOverflow(newLiteral))
                    {
                        RotateFromRight(node, rightOperator, newLiteral);
                    }
                    else
                    {
                        ConstantWrapper rightRight = rightOperator.Operand2 as ConstantWrapper;
                        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
                ConstantWrapper newLiteral = Minus(otherConstant, thisConstant);
                if (newLiteral != null && NoOverflow(newLiteral))
                {
                    rightOperator.SwapOperands();
                    RotateFromLeft(node, rightOperator, newLiteral);
                }
                else
                {
                    ConstantWrapper rightRight = rightOperator.Operand2 as ConstantWrapper;
                    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
                ConstantWrapper 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
                    ConstantWrapper 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
                    ConstantWrapper leftOverRight = Divide(thisConstant, otherConstant);
                    ConstantWrapper 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);
                        }
                    }
                }
            }
        }
コード例 #21
0
 public void Visit(BinaryOperator node)
 {
     // lesser precedence than the new operator; use parens
     m_needsParens = true;
 }
コード例 #22
0
 public void Visit(BinaryOperator node)
 {
     // invalid! ignore
     IsValid = false;
 }
コード例 #23
0
        private void Optimize(Conditional 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 UnaryOperator;
            if (unary != null && unary.OperatorToken == JSToken.LogicalNot
                && !unary.OperatorInConditionalCompilationComment
                && m_parser.Settings.IsModificationAllowed(TreeModifications.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 BinaryOperator;
            if (trueAssign != null && trueAssign.IsAssign)
            {
                var falseAssign = node.FalseExpression as BinaryOperator;
                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
                        DetachReferences.Apply(falseAssign.Operand1);

                        // transform: cond?lhs=expr1:lhs=expr2 to lhs=cond?expr1:expr2s
                        var binaryOp = new BinaryOperator(node.Context, m_parser)
                            {
                                Operand1 = trueAssign.Operand1,
                                Operand2 = new Conditional(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);
                    }
                }
            }
        }