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); } }
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) } } } }
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; } } } }
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; } }
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); } } } } }
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); } } } }
public void Visit(BinaryOperator node) { // not applicable; terminate }
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); } } }
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 } }
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; } } } }
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; }
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); }
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; } }
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); } } }
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; } }
/// <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); } }
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; } }
/// <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; }
public override void Visit(BinaryOperator node) { if (node != null) { if (m_measure) { // measure MeasureBinaryOperator(node); } else { // convert ConvertBinaryOperator(node); } } }
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); } } } } }
public void Visit(BinaryOperator node) { // lesser precedence than the new operator; use parens m_needsParens = true; }
public void Visit(BinaryOperator node) { // invalid! ignore IsValid = false; }
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); } } } }