public void Visit(Conditional node) { if (node != null) { DoesRequire = true; } }
public void Visit(Conditional node) { // if there's a condition node, recurse into it if (node != null && node.Condition != null) { node.Condition.Accept(this); } }
internal Conditional CanBeReturnOperand(AstNode ultimateOperand, bool isFunctionLevel) { Conditional conditional = null; try { if (TrueBlock != null && TrueBlock.Count == 1) { ReturnNode returnNode = TrueBlock[0] as ReturnNode; if (returnNode != null) { AstNode expr1 = returnNode.Operand; if (FalseBlock == null || FalseBlock.Count == 0) { // no false branch to speak of. Convert to conditional. // if there is an ultimate expression, use it. // if we are not at the function body level, we can't // combine these. But if we are we can // use a false expression of "void 0" (undefined) if (ultimateOperand != null || isFunctionLevel) { conditional = new Conditional( (Context == null ? null : Context.Clone()), Parser, Condition, expr1 ?? CreateVoidNode(), ultimateOperand ?? CreateVoidNode()); } } else if (FalseBlock.Count == 1) { // there is a false branch with only a single statement // see if it is a return statement returnNode = FalseBlock[0] as ReturnNode; if (returnNode != null) { // it is. so we have if(cond)return expr1;else return expr2 // return cond?expr1:expr2 AstNode expr2 = returnNode.Operand; conditional = new Conditional( (Context == null ? null : Context.Clone()), Parser, Condition, expr1 ?? CreateVoidNode(), expr2 ?? CreateVoidNode()); } else { // see if it's another if-statement IfNode elseIf = FalseBlock[0] as IfNode; if (elseIf != null) { // it's a nested if-statement. See if IT can be a return argument. Conditional expr2 = elseIf.CanBeReturnOperand(ultimateOperand, isFunctionLevel); if (expr2 != null) { // it can, so we can just nest the conditionals conditional = new Conditional( (Context == null ? null : Context.Clone()), Parser, Condition, expr1, expr2); } } // else neither return- nor if-statement } } // else false branch has more than one statement } // else the single statement is not a return-statement } // else no true branch, or not a single statement in the branch } catch (NotImplementedException) { // one of the clone calls probably failed. // don't say this can be a return argument. } return(conditional); }
public void Visit(Conditional node) { // lesser precedence than the new operator; use parens m_needsParens = true; }
private AstNode ParseExpression(AstNode leftHandSide, bool single, bool bCanAssign, JSToken inToken) { // new op stack with dummy op Stack<JSToken> opsStack = new Stack<JSToken>(); opsStack.Push(JSToken.None); // term stack, push left-hand side onto it Stack<AstNode> termStack = new Stack<AstNode>(); termStack.Push(leftHandSide); AstNode expr = null; try { for (; ; ) { // if 'binary op' or 'conditional' but not 'comma' // inToken is a special case because of the for..in crap. When ParseExpression is called from // for, inToken = JSToken.In which excludes JSToken.In from the list of operators, otherwise // inToken = JSToken.None which is always true if the first condition is true if (JSScanner.IsProcessableOperator(m_currentToken.Token) && inToken != m_currentToken.Token) { OpPrec prec = JSScanner.GetOperatorPrecedence(m_currentToken.Token); bool rightAssoc = JSScanner.IsRightAssociativeOperator(m_currentToken.Token); // the current operator has lower precedence than the operator at the top of the stack // or it has the same precedence and it is left associative (that is, no 'assign op' or 'conditional') OpPrec stackPrec = JSScanner.GetOperatorPrecedence(opsStack.Peek()); while (prec < stackPrec || prec == stackPrec && !rightAssoc) { AstNode operand2 = termStack.Pop(); AstNode operand1 = termStack.Pop(); //Console.Out.WriteLine("lower prec or same and left assoc"); expr = CreateExpressionNode(opsStack.Pop(), operand1, operand2); // push node onto the stack termStack.Push(expr); stackPrec = JSScanner.GetOperatorPrecedence(opsStack.Peek()); } // the current operator has higher precedence that every scanned operators on the stack, or // it has the same precedence as the one at the top of the stack and it is right associative // push operator and next term // special case conditional '?:' if (JSToken.ConditionalIf == m_currentToken.Token) { //Console.Out.WriteLine("Condition expression"); // pop term stack AstNode condition = termStack.Pop(); GetNextToken(); // get expr1 in logOrExpr ? expr1 : expr2 AstNode operand1 = ParseExpression(true); if (JSToken.Colon != m_currentToken.Token) ReportError(JSError.NoColon); GetNextToken(); // get expr2 in logOrExpr ? expr1 : expr2 AstNode operand2 = ParseExpression(true, inToken); expr = new Conditional(condition.Context.CombineWith(operand2.Context), this, condition, operand1, operand2); termStack.Push(expr); } else { //Console.Out.WriteLine("higher prec or right assoc"); if (JSScanner.IsAssignmentOperator(m_currentToken.Token)) { if (!bCanAssign) { ReportError(JSError.IllegalAssignment); SkipTokensAndThrow(); } } else bCanAssign = false; // push the operator onto the operators stack opsStack.Push(m_currentToken.Token); // push new term GetNextToken(); if (bCanAssign) termStack.Push(ParseUnaryExpression(out bCanAssign, false)); else { bool dummy; termStack.Push(ParseUnaryExpression(out dummy, false)); } } } else break; // done, go and unwind the stack of expressions/operators } //Console.Out.WriteLine("unwinding stack"); // there are still operators to be processed while (opsStack.Peek() != JSToken.None) { AstNode operand2 = termStack.Pop(); AstNode operand1 = termStack.Pop(); // make the ast operator node expr = CreateExpressionNode(opsStack.Pop(), operand1, operand2); // push node onto the stack termStack.Push(expr); } // if we have a ',' and we are not looking for a single expression reenter if (!single && JSToken.Comma == m_currentToken.Token) { //Console.Out.WriteLine("Next expr"); GetNextToken(); AstNode expr2 = ParseExpression(false, inToken); AstNode term = termStack.Pop(); termStack.Push(new BinaryOperator(term.Context.CombineWith(expr2.Context), this, term, expr2, JSToken.Comma)); } Debug.Assert(termStack.Count == 1); return termStack.Pop(); } catch (RecoveryTokenException exc) { exc._partiallyComputedNode = leftHandSide; throw; } }
internal override void AnalyzeNode() { // javascript doesn't have block scope, so there really is no point // in nesting blocks. Unnest any now, before we start combining var statements UnnestBlocks(); // if we want to remove debug statements... if (Parser.Settings.StripDebugStatements && Parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements)) { // do it now before we try doing other things StripDebugStatements(); } // these variables are used to check for combining a particular type of // for-statement with preceding var-statements. ForNode targetForNode = null; string targetName = null; // check to see if we want to combine adjacent var statements bool combineVarStatements = Parser.Settings.IsModificationAllowed(TreeModifications.CombineVarStatements); // check to see if we want to combine a preceding var with a for-statement bool moveVarIntoFor = Parser.Settings.IsModificationAllowed(TreeModifications.MoveVarIntoFor); // look at the statements in the block. // if there are multiple var statements adjacent to each other, combine them. // walk BACKWARDS down the list because we'll be removing items when we encounter // multiple vars. // we also don't need to check the first one, since there is nothing before it. for (int ndx = m_list.Count - 1; ndx > 0; --ndx) { // if the previous node is not a Var, then we don't need to try and combine // it withthe current node Var previousVar = m_list[ndx - 1] as Var; if (previousVar != null) { // see if THIS item is also a Var... if (m_list[ndx] is Var && combineVarStatements) { // add the items in this VAR to the end of the previous previousVar.Append(m_list[ndx]); // delete this item from the block m_list.RemoveAt(ndx); // if we have a target for-node waiting for another comparison.... if (targetForNode != null) { // check to see if the variable we are looking for is in the new list if (previousVar.Contains(targetName)) { // IT DOES! we can combine the var statement with the initializer in the for-statement // we already know it's a binaryop, or it wouldn't be a target for-statement BinaryOperator binaryOp = targetForNode.Initializer as BinaryOperator; // create a vardecl that matches our assignment initializer // ignore duplicates because this scope will already have the variable defined. VariableDeclaration varDecl = new VariableDeclaration( binaryOp.Context.Clone(), Parser, targetName, binaryOp.Operand1.Context.Clone(), binaryOp.Operand2, 0, true ); // append it to the preceding var-statement previousVar.Append(varDecl); // move the previous vardecl to our initializer targetForNode.ReplaceChild(targetForNode.Initializer, previousVar); // and remove the previous var from the list. m_list.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 // but now we no longer need the target mechanism -- the for-statement is // not the current node again targetForNode = null; } } } else if (moveVarIntoFor) { // see if this item is a ForNode ForNode forNode = m_list[ndx] as ForNode; if (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) { // we want to PREPEND the initializers in the previous var statement // to our for-statement's initializer list varInitializer.InsertAt(0, previousVar); // then remove the previous var statement m_list.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 { // see if the initializer is a simple assignment BinaryOperator binaryOp = forNode.Initializer as BinaryOperator; if (binaryOp != null && binaryOp.OperatorToken == JSToken.Assign) { // it is. See if it's a simple lookup Lookup lookup = binaryOp.Operand1 as Lookup; if (lookup != null) { // it is. see if that variable is in the previous var statement if (previousVar.Contains(lookup.Name)) { // create a vardecl that matches our assignment initializer // ignore duplicates because this scope will already have the variable defined. VariableDeclaration varDecl = new VariableDeclaration( binaryOp.Context.Clone(), Parser, lookup.Name, lookup.Context.Clone(), binaryOp.Operand2, 0, true ); // append it to the var statement before us previousVar.Append(varDecl); // move the previous vardecl to our initializer forNode.ReplaceChild(forNode.Initializer, previousVar); // and remove the previous var from the list. m_list.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 { // it's not in the immediately preceding var-statement, but that doesn't mean it won't be in // a var-statement immediately preceding that one -- in which case they'll get combined and // then it WILL be in the immediately preceding var-statement. So hold on to this // for statement and we'll check after we do a combine. targetForNode = forNode; targetName = lookup.Name; } } } } } else { // 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 m_list.RemoveAt(ndx - 1); forNode.ReplaceChild(forNode.Initializer, 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 } } } } else { // not a var statement. make sure the target for-node is cleared. targetForNode = null; ConditionalCompilationComment previousComment = m_list[ndx - 1] as ConditionalCompilationComment; if (previousComment != null) { ConditionalCompilationComment thisComment = m_list[ndx] as ConditionalCompilationComment; if (thisComment != null) { // two adjacent conditional comments -- combine them into the first. // this will actually make the second block a nested block within the first block, // but they'll be flattened when the comment's block gets recursed. previousComment.Statements.Append(thisComment.Statements); // and remove the second one (which is now a duplicate) m_list.RemoveAt(ndx); } } } } if (m_blockScope != null) { ScopeStack.Push(m_blockScope); } try { // call the base class to recurse base.AnalyzeNode(); } finally { if (m_blockScope != null) { ScopeStack.Pop(); } } // NOW that we've recursively analyzed all the child nodes in this block, let's see // if we can further reduce the statements by checking for a couple good opportunities if (Parser.Settings.RemoveUnneededCode) { // Transform: {var foo=expression;return foo;} to: {return expression;} if (m_list.Count == 2 && Parser.Settings.IsModificationAllowed(TreeModifications.VarInitializeReturnToReturnInitializer)) { Var varStatement = m_list[0] as Var; ReturnNode returnStatement = m_list[1] as ReturnNode; // see if we have two statements in our block: a var with a single declaration, and a return if (returnStatement != null && varStatement != null && varStatement.Count == 1 && varStatement[0].Initializer != null) { // now see if the return is returning a lookup for the same var we are declaring in the // previous statement Lookup lookup = returnStatement.Operand as Lookup; if (lookup != null && string.Compare(lookup.Name, varStatement[0].Identifier, StringComparison.Ordinal) == 0) { // it's a match! // create a combined context starting with the var and adding in the return Context context = varStatement.Context.Clone(); context.UpdateWith(returnStatement.Context); // create a new return statement ReturnNode newReturn = new ReturnNode(context, Parser, varStatement[0].Initializer); // clear out the existing statements m_list.Clear(); // and add our new one Append(newReturn); } } } // we do things differently if these statements are the last in a function // because we can assume the implicit return bool isFunctionLevel = (Parent is FunctionObject); // see if we want to change if-statement that forces a return to a return conditional if (Parser.Settings.IsModificationAllowed(TreeModifications.IfElseReturnToReturnConditional)) { // transform: {...; if(cond1)return;} to {...;cond;} // transform: {...; if(cond1)return exp1;else return exp2;} to {...;return cond1?exp1:exp2;} if (m_list.Count >= 1) { // see if the last statement is an if-statement with a true-block containing only one statement IfNode ifStatement = m_list[m_list.Count - 1] as IfNode; if (ifStatement != null && ifStatement.TrueBlock != null) { // see if this if-statement is structured such that we can convert it to a // Conditional node that is the operand of a return statement Conditional returnOperand = ifStatement.CanBeReturnOperand(null, isFunctionLevel); if (returnOperand != null) { // it can! change it. ReturnNode returnNode = new ReturnNode( (Context == null ? null : Context.Clone()), Parser, returnOperand); // replace the if-statement with the return statement ReplaceChild(ifStatement, returnNode); } } // else last statement is not an if-statement, or true block is not a single statement } // transform: {...; if(cond1)return exp1;return exp2;} to {...; return cond1?exp1:exp2;} // my cascade! changing the two statements to a return may cause us to run this again if the // third statement up becomes the penultimate and is an if-statement while (m_list.Count > 1) { int lastIndex = m_list.Count - 1; // end in a return statement? ReturnNode finalReturn = m_list[lastIndex] as ReturnNode; if (finalReturn != null) { // it does -- see if the penultimate statement is an if-block IfNode ifNode = m_list[lastIndex - 1] as IfNode; if (ifNode != null) { // if followed by return. See if the if statement can be changed to a // return of a conditional, using the operand of the following return // as the ultimate expression Conditional returnConditional = ifNode.CanBeReturnOperand(finalReturn.Operand, isFunctionLevel); if (returnConditional != null) { // it can! so create the new return statement. // the context of this new return statement should start with a clone of // the if-statement and updated with the return statement Context context = ifNode.Context.Clone(); context.UpdateWith(finalReturn.Context); // create the new return node ReturnNode newReturn = new ReturnNode( context, Parser, returnConditional); // remove the last node (the old return) m_list.RemoveAt(lastIndex--); // and replace the if-statement with the new return m_list[lastIndex] = newReturn; newReturn.Parent = this; // we collapsed the last two statements, and we KNOW the last one is a // return -- go back up to the top of the loop to see if we can keep going. continue; } } } // if we get here, then something went wrong, we didn't collapse the last // two statements, so break out of the loop break; } // now we may have converted the last functional statement // from if(cond)return expr to return cond?expr:void 0, which is four // extra bytes. So let's check to see if the last statement in the function // now fits this pattern, and if so, change it back. // We didn't just NOT change it in the first place because changing it could've // enabled even more changes that would save a lot more space. But apparently // those subsequent changes didn't pan out. if (m_list.Count >= 1) { int lastIndex = m_list.Count - 1; ReturnNode returnNode = m_list[lastIndex] as ReturnNode; if (returnNode != null) { Conditional conditional = returnNode.Operand as Conditional; if (conditional != null) { VoidNode falseVoid = conditional.FalseExpression as VoidNode; if (falseVoid != null && falseVoid.Operand is ConstantWrapper) { // we have the required pattern: "return cond?expr:void 0" // (well, the object of the void is a constant, at least). // undo it back to "if(cond)return expr" because that takes fewer bytes. // by default, the operand of the return operator will be the // true branch of the conditional AstNode returnOperand = conditional.TrueExpression; VoidNode trueVoid = conditional.TrueExpression as VoidNode; if (trueVoid != null && trueVoid.Operand is ConstantWrapper) { // the true branch of the conditional is a void operator acting // on a constant! So really, there is no operand to the return statement returnOperand = null; if (Parser.Settings.IsModificationAllowed(TreeModifications.IfConditionReturnToCondition)) { // actually, we have return cond?void 0:void 0, // which would get changed back to function{...;if(cond)return} // BUT we can just shorten it to function{...;cond} m_list[lastIndex] = conditional.Condition; conditional.Condition.Parent = this; return; } } IfNode ifNode = new IfNode( returnNode.Context.Clone(), Parser, conditional.Condition, new ReturnNode(returnNode.Context.Clone(), Parser, returnOperand), null); m_list[lastIndex] = ifNode; ifNode.Parent = this; } } } } } } }
public override void Visit(Conditional node) { if (node != null) { // analye all the children base.Visit(node); // and then optimize our node Optimize(node); } }
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 NumericUnary unary = node.Condition as NumericUnary; if (unary != null && unary.OperatorToken == JSToken.LogicalNot && m_parser.Settings.IsModificationAllowed(TreeModifications.IfNotTrueFalseToIfFalseTrue)) { // get rid of the not by replacing it with its operand if (node.ReplaceChild(node.Condition, unary.Operand)) { // and swap the branches 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)) { // transform: cond?lhs=expr1:lhs=expr2 to lhs=cond?expr1:expr2s var binaryOp = new BinaryOperator( node.Context, m_parser, trueAssign.Operand1, new Conditional( node.Context, m_parser, node.Condition, trueAssign.Operand2, falseAssign.Operand2), falseAssign.OperatorToken); node.Parent.ReplaceChild(node, binaryOp); } } } }
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); } } } } } }
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); } } } }
public void Visit(Conditional node) { ReportError(node); }
public void Visit(Conditional node) { // invalid! ignore IsValid = false; }
public override void Visit(Conditional node) { if (node != null) { // depth-first base.Visit(node); if (m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { // if the condition is a literal, evaluating the condition doesn't do anything, AND // we know now whether it's true or not. ConstantWrapper literalCondition = node.Condition as ConstantWrapper; if (literalCondition != null) { try { // if the boolean represenation of the literal is true, we can replace the condition operator // with the true expression; otherwise we can replace it with the false expression node.Parent.ReplaceChild(node, literalCondition.ToBoolean() ? node.TrueExpression : node.FalseExpression); } catch (InvalidCastException) { // ignore any invalid cast errors } } } } }
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); } } }