Пример #1
0
        public void NotExpressions()
        {
            var expressionSource = TestContext.DataRow[0].ToString();
            var expectedResult   = TestContext.DataRow[1].ToString();

            // parse the source into an AST
            var parser = new JSParser();
            var block  = parser.Parse(expressionSource, new CodeSettings()
            {
                MinifyCode = false, SourceMode = JavaScriptSourceMode.Expression
            });

            if (block.Count == 1)
            {
                var expression = block[0];

                // create the logical-not visitor on the expression
                var logicalNot = new Microsoft.Ajax.Utilities.LogicalNot(expression, parser.Settings);

                // get the original code
                var original = OutputVisitor.Apply(expression, parser.Settings);

                Trace.Write("ORIGINAL EXPRESSION:    ");
                Trace.WriteLine(original);

                // get the measured delta
                var measuredDelta = logicalNot.Measure();

                // perform the logical-not operation
                logicalNot.Apply();

                // get the resulting code -- should still be only one statement in the block
                var notted = OutputVisitor.Apply(block[0], parser.Settings);

                Trace.Write("LOGICAL-NOT EXPRESSION: ");
                Trace.WriteLine(notted);

                Trace.Write("EXPECTED EXPRESSION:    ");
                Trace.WriteLine(expectedResult);

                Trace.Write("DELTA: ");
                Trace.WriteLine(measuredDelta);

                // what's the actual difference
                var actualDelta = notted.Length - original.Length;
                Assert.AreEqual(actualDelta, measuredDelta,
                                "Measurement was off; calculated {0} but was actually {1}",
                                measuredDelta,
                                actualDelta);

                Assert.AreEqual(expectedResult, notted, "Expected output is not the same!!!!");
            }
            else
            {
                Assert.Fail(string.Format("Source line '{0}' parsed to more than one statement!", expressionSource));
            }
        }
Пример #2
0
        public override void Visit(Block node)
        {
            if (node != null)
            {
                // we might things differently if these statements are the body collection for a function
                // because we can assume the implicit return statement at the end of it
                bool isFunctionLevel = (node.Parent is FunctionObject);

                // if we want to remove debug statements...
                if (m_parser.Settings.StripDebugStatements && m_parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements))
                {
                    // do it now before we try doing other things
                    StripDebugStatements(node);
                }

                // analyze all the statements in our block and recurse them
                if (node.BlockScope != null)
                {
                    ScopeStack.Push(node.BlockScope);
                }
                try
                {
                    // call the base class to recurse
                    base.Visit(node);
                }
                finally
                {
                    if (node.BlockScope != null)
                    {
                        ScopeStack.Pop();
                    }
                }

                if (m_parser.Settings.RemoveUnneededCode)
                {
                    // go forward, and check the count each iteration because we might be ADDING statements to the block.
                    // let's look at all our if-statements. If a true-clause ends in a return, then we don't
                    // need the else-clause; we can pull its statements out and stick them after the if-statement.
                    // also, if we encounter a return-, break- or continue-statement, we can axe everything after it
                    for (var ndx = 0; ndx < node.Count; ++ndx)
                    {
                        // see if it's an if-statement with both a true and a false block
                        var ifNode = node[ndx] as IfNode;
                        if (ifNode != null
                            && ifNode.TrueBlock != null
                            && ifNode.TrueBlock.Count > 0
                            && ifNode.FalseBlock != null)
                        {
                            // now check to see if the true block ends in a return statement
                            if (ifNode.TrueBlock[ifNode.TrueBlock.Count - 1] is ReturnNode)
                            {
                                // transform: if(cond){statements1;return}else{statements2} to if(cond){statements1;return}statements2
                                // it does. insert all the false-block statements after the if-statement
                                node.InsertRange(ndx + 1, ifNode.FalseBlock.Children);

                                // and then remove the false block altogether
                                ifNode.ReplaceChild(ifNode.FalseBlock, null);
                            }
                        }
                        else if (node[ndx] is ReturnNode
                            || node[ndx] is Break
                            || node[ndx] is ContinueNode)
                        {
                            // we have a return node -- no statments afterwards will be executed, so clear them out.
                            // transform: {...;return;...} to {...;return}
                            // transform: {...;break;...} to {...;break}
                            // transform: {...;continue;...} to {...;continue}
                            // we've found a return statement, and it's not the last statement in the function.
                            // walk the rest of the statements and delete anything that isn't a function declaration
                            // or a var statement.
                            for (var ndxRemove = node.Count - 1; ndxRemove > ndx; --ndxRemove)
                            {
                                var funcObject = node[ndxRemove] as FunctionObject;
                                if (funcObject == null || funcObject.FunctionType != FunctionType.Declaration)
                                {
                                    var varStatement = node[ndxRemove] as Var;
                                    if (varStatement != null)
                                    {
                                        // var statements can't be removed, but any initializers should
                                        // be deleted since they won't get executed.
                                        for (var ndxDecl = 0; ndxDecl < varStatement.Count; ++ndxDecl)
                                        {
                                            if (varStatement[ndxDecl].Initializer != null)
                                            {
                                                varStatement[ndxDecl].ReplaceChild(varStatement[ndxDecl].Initializer, null);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // not a function declaration, and not a var statement -- get rid of it
                                        node.RemoveAt(ndxRemove);
                                    }
                                }
                            }
                        }
                    }
                }

                // now check the last statement -- if it's an if-statement where the true-block is a single return
                // and there is no false block, convert this one statement to a conditional. We might back it out later
                // if we don't combine the conditional with other stuff.
                // but we can only do this if we're at the functional level because of the implied return at the end
                // of that block.
                if (isFunctionLevel && node.Count > 0
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.IfConditionReturnToCondition))
                {
                    ReturnNode returnNode;
                    var ifNode = FindLastStatement(node) as IfNode;
                    if (ifNode != null && ifNode.FalseBlock == null
                        && ifNode.TrueBlock.Count == 1
                        && (returnNode = ifNode.TrueBlock[0] as ReturnNode) != null)
                    {
                        // if the return node doesn't have an operand, then we can just replace the if-statement with its conditional
                        if (returnNode.Operand == null)
                        {
                            // transform: if(cond)return;} to cond}
                            // TODO: if the condition is a constant, then eliminate it altogether
                            node.ReplaceChild(ifNode, ifNode.Condition);
                        }
                        else if (returnNode.Operand.IsExpression)
                        {
                            // transform: if(cond)return expr;} to return cond?expr:void 0}
                            var conditional = new Conditional(
                                null, m_parser, ifNode.Condition,
                                returnNode.Operand,
                                CreateVoidNode());

                            // replace the if-statement with the new return node
                            node.ReplaceChild(ifNode, new ReturnNode(ifNode.Context, m_parser, conditional));
                            Optimize(conditional);
                        }
                    }
                }

                // now walk through and combine adjacent expression statements, and adjacent var-for statements
                // and adjecent expression-return statements
                if (m_parser.Settings.IsModificationAllowed(TreeModifications.CombineAdjacentExpressionStatements))
                {
                    CombineExpressions(node);
                }

                // check to see if we want to combine a preceding var with a for-statement
                if (m_parser.Settings.IsModificationAllowed(TreeModifications.MoveVarIntoFor))
                {
                    // look at the statements in the block.
                    // walk BACKWARDS down the list because we'll be removing items when we encounter
                    // var statements that can be moved inside a for statement's initializer
                    // we also don't need to check the first one, since there is nothing before it.
                    for (int ndx = node.Count - 1; ndx > 0; --ndx)
                    {
                        // see if the previous statement is a var statement
                        // (we've already combined adjacent var-statements)
                        ForNode forNode;
                        var previousVar = node[ndx - 1] as Var;
                        if (previousVar != null && (forNode = node[ndx] as ForNode) != null)
                        {
                            // and see if the forNode's initializer is empty
                            if (forNode.Initializer != null)
                            {
                                // not empty -- see if it is a Var node
                                Var varInitializer = forNode.Initializer as Var;
                                if (varInitializer != null)
                                {
                                    // transform: var decls1;for(var decls2;...) to for(var decls1,decls2;...)
                                    // we want to PREPEND the initializers in the previous var-statement
                                    // to our for-statement's initializer var-statement list
                                    varInitializer.InsertAt(0, previousVar);

                                    // then remove the previous var statement
                                    node.RemoveAt(ndx - 1);
                                    // this will bump the for node up one position in the list, so the next iteration
                                    // will be right back on this node in case there are other var statements we need
                                    // to combine
                                }
                                else
                                {
                                    // we want to see if the initializer expression is a series of one or more
                                    // simple assignments to variables that are in the previous var statement.
                                    // if all the expressions are assignments to variables that are defined in the
                                    // previous var statement, then we can just move the var statement into the
                                    // for statement.
                                    BinaryOperator binaryOp = forNode.Initializer as BinaryOperator;
                                    if (binaryOp != null && AreAssignmentsInVar(binaryOp, previousVar))
                                    {
                                        // transform: var decls;for(expr1;...) to for(var decls,expr1;...)
                                        // create a list and fill it with all the var-decls created from the assignment
                                        // operators in the expression
                                        var varDecls = new List<VariableDeclaration>();
                                        ConvertAssignmentsToVarDecls(binaryOp, varDecls, m_parser);

                                        // then go through and append each one to the var statement before us
                                        foreach (var varDecl in varDecls)
                                        {
                                            previousVar.Append(varDecl);
                                        }

                                        // move the previous var-statement into our initializer
                                        forNode.ReplaceChild(forNode.Initializer, previousVar);

                                        // and remove the previous var-statement from the list.
                                        node.RemoveAt(ndx - 1);

                                        // this will bump the for node up one position in the list, so the next iteration
                                        // will be right back on this node, but the initializer will not be null
                                    }
                                }
                            }
                            else
                            {
                                // transform: var decls;for(;...) to for(var decls;...)
                                // if it's empty, then we're free to add the previous var statement
                                // to this for statement's initializer. remove it from it's current
                                // position and add it as the initializer
                                node.RemoveAt(ndx - 1);
                                forNode.SetInitializer(previousVar);
                                // this will bump the for node up one position in the list, so the next iteration
                                // will be right back on this node, but the initializer will not be null
                            }
                        }
                    }
                }

                // see if the last statement is a return statement
                ReturnNode lastReturn;
                if ((lastReturn = FindLastStatement(node) as ReturnNode) != null)
                {
                    // set this flag to true if we end up adding an expression to the block.
                    // before exiting, we'll go through and combine adjacent expressions again if this
                    // flag has been set to true.
                    bool changedStatementToExpression = false;

                    // get the index of the statement before the last return
                    // (skip over function decls and importand comments)
                    var indexPrevious = PreviousStatementIndex(node, lastReturn);

                    // just out of curiosity, let's see if we fit a common pattern:
                    //      var name=expr;return name;
                    // if so, we can cut out the var and simply return the expression
                    Lookup lookup;
                    if ((lookup = lastReturn.Operand as Lookup) != null && indexPrevious >= 0)
                    {
                        var varStatement = node[indexPrevious] as Var;
                        if (varStatement != null)
                        {
                            // if the last vardecl in the var statement matches the return lookup, and no
                            // other references exist for this field (refcount == 1)...
                            VariableDeclaration varDecl;
                            if ((varDecl = varStatement[varStatement.Count - 1]).Initializer != null
                                && varDecl.IsEquivalentTo(lookup)
                                && varDecl.Field.RefCount == 1)
                            {
                                if (varStatement.Count == 1)
                                {
                                    // transform: ...;var name=expr;return name} to ...;return expr}
                                    // there's only one vardecl in the var, so get rid of the entire statement
                                    lastReturn.ReplaceChild(lookup, varDecl.Initializer);
                                    node.RemoveAt(indexPrevious);
                                }
                                else
                                {
                                    // multiple vardecls are in the statement; we only need to get rid of the last one
                                    lastReturn.ReplaceChild(lookup, varDecl.Initializer);
                                    varStatement.ReplaceChild(varDecl, null);
                                }
                            }
                        }
                    }

                    // check to see if we can combine the return statement with a previous if-statement
                    // into a simple return-conditional. The true statement needs to have no false block,
                    // and only one statement in the true block.
                    Conditional conditional;
                    IfNode previousIf;
                    while (indexPrevious >= 0
                        && lastReturn != null
                        && (previousIf = node[indexPrevious] as IfNode) != null
                        && previousIf.TrueBlock != null && previousIf.TrueBlock.Count == 1
                        && previousIf.FalseBlock == null)
                    {
                        // assume no change is made for this loop
                        bool somethingChanged = false;

                        // and that one true-block statement needs to be a return statement
                        var previousReturn = previousIf.TrueBlock[0] as ReturnNode;
                        if (previousReturn != null)
                        {
                            if (lastReturn.Operand == null)
                            {
                                if (previousReturn.Operand == null)
                                {
                                    // IF we are at the function level, then the block ends in an implicit return (undefined)
                                    // and we can change this if to just the condition. If we aren't at the function level,
                                    // then we have to leave the return, but we can replace the if with just the condition.
                                    if (!isFunctionLevel)
                                    {
                                        // transform: if(cond)return;return} to cond;return}
                                        node.ReplaceChild(previousIf, previousIf.Condition);
                                    }
                                    else
                                    {
                                        // transform: if(cond)return;return} to cond}
                                        // replace the final return with just the condition, then remove the previous if
                                        if (node.ReplaceChild(lastReturn, previousIf.Condition))
                                        {
                                            node.RemoveAt(indexPrevious);
                                            somethingChanged = true;
                                        }
                                    }
                                }
                                else
                                {
                                    // transform: if(cond)return expr;return} to return cond?expr:void 0
                                    conditional = new Conditional(null, m_parser,
                                        previousIf.Condition,
                                        previousReturn.Operand,
                                        CreateVoidNode());

                                    // replace the final return with the new return, then delete the previous if-statement
                                    if (node.ReplaceChild(lastReturn, new ReturnNode(null, m_parser, conditional)))
                                    {
                                        node.RemoveAt(indexPrevious);
                                        Optimize(conditional);
                                        somethingChanged = true;
                                    }
                                }
                            }
                            else
                            {
                                if (previousReturn.Operand == null)
                                {
                                    // transform: if(cond)return;return expr} to return cond?void 0:expr
                                    conditional = new Conditional(null, m_parser,
                                        previousIf.Condition,
                                        CreateVoidNode(),
                                        lastReturn.Operand);

                                    // replace the final return with the new return, then delete the previous if-statement
                                    if (node.ReplaceChild(lastReturn, new ReturnNode(null, m_parser, conditional)))
                                    {
                                        node.RemoveAt(indexPrevious);
                                        Optimize(conditional);
                                        somethingChanged = true;
                                    }
                                }
                                else if (previousReturn.Operand.IsEquivalentTo(lastReturn.Operand))
                                {
                                    // transform: if(cond)return expr;return expr} to return cond,expr}
                                    // create a new binary op with the condition and the final-return operand,
                                    // replace the operand on the final-return with the new binary operator,
                                    // and then delete the previous if-statement
                                    if (lastReturn.ReplaceChild(lastReturn.Operand,
                                        new BinaryOperator(null, m_parser, previousIf.Condition, lastReturn.Operand, JSToken.Comma)))
                                    {
                                        node.RemoveAt(indexPrevious);
                                        somethingChanged = true;
                                    }
                                }
                                else
                                {
                                    // transform: if(cond)return expr1;return expr2} to return cond?expr1:expr2}
                                    // create a new conditional with the condition and the return operands,
                                    // replace the operand on the final-return with the new conditional operator,
                                    // and then delete the previous if-statement
                                    // transform: if(cond)return expr1;return expr2} to return cond?expr1:expr2}
                                    conditional = new Conditional(null, m_parser,
                                        previousIf.Condition, previousReturn.Operand, lastReturn.Operand);

                                    // replace the operand on the final-return with the new conditional operator,
                                    // and then delete the previous if-statement
                                    if (lastReturn.ReplaceChild(lastReturn.Operand, conditional))
                                    {
                                        node.RemoveAt(indexPrevious);
                                        Optimize(conditional);
                                        somethingChanged = true;
                                    }
                                }
                            }
                        }

                        if (!somethingChanged)
                        {
                            // nothing changed -- break out of the loop
                            break;
                        }
                        else
                        {
                            // set the flag that indicates something changed in at least one of these loops
                            changedStatementToExpression = true;

                            // and since we changed something, we need to bump the index down one
                            // AFTER we grab the last return node (which has slipped into the same position
                            // as the previous node)
                            lastReturn = node[indexPrevious--] as ReturnNode;
                        }
                    }

                    // if we added any more expressions since we ran our expression-combination logic,
                    // run it again.
                    if (changedStatementToExpression
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.CombineAdjacentExpressionStatements))
                    {
                        CombineExpressions(node);
                    }

                    // and FINALLY, we want to see if what we did previously didn't pan out and we end
                    // in something like return cond?expr:void 0, in which case we want to change it
                    // back to a simple if(condition)return expr; (saves four bytes).
                    // see if the last statement is a return statement that returns a conditional
                    if (lastReturn != null
                        && (conditional = lastReturn.Operand as Conditional) != null)
                    {
                        VoidNode voidOperator = conditional.FalseExpression as VoidNode;
                        if (voidOperator != null && voidOperator.Operand is ConstantWrapper)
                        {
                            voidOperator = conditional.TrueExpression as VoidNode;
                            if (voidOperator != null)
                            {
                                if (isFunctionLevel)
                                {
                                    // transform: ...;return cond?void 0:void 0} to ...;cond}
                                    // function level ends in an implicit "return void 0"
                                    node.ReplaceChild(lastReturn, conditional.Condition);
                                }
                                else
                                {
                                    // transform: ...;return cond?void 0:void 0} to ...;cond;return}
                                    // non-function level doesn't end in an implicit return,
                                    // so we need to break them out into two statements
                                    node.ReplaceChild(lastReturn, conditional.Condition);
                                    node.Append(new ReturnNode(null, m_parser, null));
                                }
                            }
                            else if (isFunctionLevel)
                            {
                                // transform: ...;return cond?expr:void 0} to ...;if(cond)return expr}
                                // (only works at the function-level because of the implicit return statement)
                                var ifNode = new IfNode(lastReturn.Context,
                                    m_parser,
                                    conditional.Condition,
                                    new ReturnNode(null, m_parser, conditional.TrueExpression),
                                    null);
                                node.ReplaceChild(lastReturn, ifNode);
                            }
                        }
                        else if (isFunctionLevel)
                        {
                            voidOperator = conditional.TrueExpression as VoidNode;
                            if (voidOperator != null && voidOperator.Operand is ConstantWrapper)
                            {
                                // transform: ...;return cond?void 0;expr} to ...;if(!cond)return expr}
                                // (only works at the function level because of the implicit return)
                                // get the logical-not of the conditional
                                var logicalNot = new LogicalNot(conditional.Condition, m_parser);
                                logicalNot.Apply();

                                // create a new if-node based on the condition, with the branches swapped
                                // (true-expression goes to false-branch, false-expression goes to true-branch
                                var ifNode = new IfNode(lastReturn.Context,
                                    m_parser,
                                    conditional.Condition,
                                    new ReturnNode(null, m_parser, conditional.FalseExpression),
                                    null);
                                node.ReplaceChild(lastReturn, ifNode);
                            }
                        }
                    }
                }

            }
        }
Пример #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.ReplaceChild(node.TrueBlock, null);
                    }

                    if (node.FalseBlock != null && node.FalseBlock.IsDebuggerStatement)
                    {
                        node.ReplaceChild(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.ReplaceChild(node.TrueBlock, null);
                }
                if (node.FalseBlock != null && node.FalseBlock.Count == 0)
                {
                    node.ReplaceChild(node.FalseBlock, 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.TrueBlock == null && node.FalseBlock != null
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.IfConditionFalseToIfNotConditionTrue))
                {
                    // logical-not the condition
                    var logicalNot = new LogicalNot(node.Condition, m_parser);
                    logicalNot.Apply();

                    // and swap the branches
                    node.SwapBranches();
                }
                else if (node.TrueBlock != null && node.FalseBlock != null)
                {
                    // 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,
                                    node.Condition,
                                    trueReturn.Operand,
                                    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,
                                    conditional);

                                node.Parent.ReplaceChild(
                                    node,
                                    returnNode);

                                Optimize(conditional);
                            }
                        }
                    }
                }
                else if (node.TrueBlock == null && node.FalseBlock == null
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.IfEmptyToExpression))
                {
                    // NEITHER branches have anything now!

                    // something we can do in the future: 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.

                    // I'm just not doing it yet because I don't
                    // know what the effect will be on the iteration of block statements.
                    // if we're on item, 5, for instance, and we delete it, will the next
                    // item be item 6, or will it return the NEW item 5 (since the old item
                    // 5 was deleted and everything shifted up)?

                    // 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
                    node.Parent.ReplaceChild(node, node.Condition);
                    // no need to analyze -- we already recursed
                }

                // if the true block is not empty, but it's an expression, there are a couple more
                // optimizations we can make
                if (node.TrueBlock != null && node.TrueBlock.IsExpression
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.IfExpressionsToExpression))
                {
                    if (node.FalseBlock != null && node.FalseBlock.IsExpression)
                    {
                        // 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,
                                node.Condition,
                                node.FalseBlock[0],
                                node.TrueBlock[0]);
                        }
                        else
                        {
                            // regular order
                            conditional = new Conditional(
                                node.Context,
                                m_parser,
                                node.Condition,
                                node.TrueBlock[0],
                                node.FalseBlock[0]);
                        }

                        node.Parent.ReplaceChild(
                            node,
                            conditional);

                        Optimize(conditional);
                    }
                    else if (node.FalseBlock == null
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.IfConditionCallToConditionAndCall))
                    {
                        // 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(node.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(
                            node.Context,
                            m_parser,
                            node.Condition,
                            node.TrueBlock[0],
                            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);
                    }
                }
            }
        }
Пример #4
0
        private void RunTest(CodeSettings settings)
        {
            var source = GetSource(".js");
            var expected = GetExpected(".js");

            settings = settings ?? new CodeSettings() { MinifyCode = false };

            if (source.Length == expected.Length)
            {
                for (var ndx = 0; ndx < source.Length; ++ndx)
                {
                    Trace.WriteLine("");
                    Trace.WriteLine("----------------------------------------------------------------------------");
                    Trace.WriteLine("");

                    // parse the source into an AST
                    var parser = new JSParser(source[ndx]);
                    var block = parser.Parse(settings);

                    // there should only be one statement in the block
                    if (block.Count == 1)
                    {
                        var expression = block[0];

                        // create the logical-not visitor on the expression
                        var logicalNot = new Microsoft.Ajax.Utilities.LogicalNot(expression, parser);

                        // get the original code
                        var original = expression.ToCode();

                        Trace.Write("ORIGINAL EXPRESSION:    ");
                        Trace.WriteLine(original);

                        // get the measured delta
                        var measuredDelta = logicalNot.Measure();

                        // perform the logical-not operation
                        logicalNot.Apply();

                        // get the resulting code -- should still be only one statement in the block
                        var notted = block[0].ToCode();

                        Trace.Write("LOGICAL-NOT EXPRESSION: ");
                        Trace.WriteLine(notted);

                        Trace.Write("EXPECTED EXPRESSION:    ");
                        Trace.WriteLine(expected[ndx]);

                        Trace.Write("DELTA: ");
                        Trace.WriteLine(measuredDelta);

                        // what's the actual difference
                        var actualDelta = notted.Length - original.Length;
                        Assert.IsTrue(actualDelta == measuredDelta,
                            "Measurement was off; calculated {0} but was actually {1}",
                            measuredDelta,
                            actualDelta);

                        Assert.IsTrue(string.CompareOrdinal(expected[ndx], notted) == 0, "Expected output is not the same!!!!");
                    }
                    else
                    {
                        Assert.Fail(string.Format("Source line {0} parsed to more than one statement!", ndx + 1));
                    }
                }
            }
            else
            {
                Assert.Fail("Input and Expected files have different number of lines!");
            }
        }
Пример #5
0
        public override void Visit(Conditional node)
        {
            if (node != null)
            {
                // we have two choices for the conditional. Either:
                //  1. we wrap the whole thing in a logical-not operator, which means we also need to
                //     add parentheses, since conditional is lower-precedence than the logicial not, or
                //  2. apply the logical-not to both the true- and false-branches.
                // The first is guaranteed 3 additional characters. We have to check the delta for
                // each branch and add them together to know how much the second would cost. If it's
                // greater than 3, then we just want to not the whole thing.
                var notTrue = new LogicalNot(node.TrueExpression, m_parser);
                var notFalse = new LogicalNot(node.FalseExpression, m_parser);
                var costNottingBoth = notTrue.Measure() + notFalse.Measure();

                if (m_measure)
                {
                    // we're just measuring -- adjust the delta accordingly
                    // (the lesser of the two options)
                    m_delta += (costNottingBoth > 3) ? 3 : costNottingBoth;
                }
                else if (costNottingBoth > 3)
                {
                    // just wrap the whole thing
                    WrapWithLogicalNot(node);
                }
                else
                {
                    // less bytes to wrap each branch separately
                    node.TrueExpression.Accept(this);
                    node.FalseExpression.Accept(this);
                }
            }
        }