/// <summary> /// Collapses unnecessary double-branches into a single branch. /// </summary> /// <param name="node">Node that possibly begins a double-branch.</param> /// <returns>True if a double Branch was collapsed; false otherwise.</returns> private static bool CollapseDoubleBranches(BlockNode node) { // Sometimes a method body can have blocks like: // [ .... ] // [branch] // | // | // | // [branch] // | // | // | // [ .... ] // // We collapse these into: // [ .... ] // [branch] // | // | // | // [ .... ] if (node.Outgoing.Count == 1) { BlockNode target = node.Graph[node.Outgoing[0].Target]; if (target.Outgoing.Count == 1 && (target.Block.Statements.Count == 0 || target.Block.Statements.Count == 1 && target.Block.Statements[0] is Branch)) { RemoveStatements(node.Block.Statements, node.Outgoing[0]); node.Block.Statements.Add(target.Outgoing[0]); node.Refresh(); if (target.Incoming.Count == 0) { node.Graph.Remove(target); } return(true); } } return(false); }
/// <summary> /// Looks for normalized ternary expression patterns and creates TernaryExpressions. /// </summary> /// <param name="node">Node that possibly begins a normalized ternary expression.</param> /// <returns>True if a ternary expression was abnormalized; false otherwise.</returns> private static bool AbnormalizeExpression(BlockNode node) { // f(a ? b : c) is: f(a && b) is: f(a || b) is: // [ ... ] [ ... ] [ ... ] // [if (a) branch] [if (!a) branch] [if (a) branch] // / \ / \ / \ // /a \fallthrough /!a \fallthrough /a \fallthrough // / \ / \ / \ // [b] [c] [0] [b] [1] [b] // \ / \ / \ / // \ / \ / \ / // \ / \ / \ / // [f(pop)] [f(pop)] [f(pop)] // [ .... ] [ .... ] [ .... ] BlockGraph graph = node.Graph; // First check the structure of head. if (node.Outgoing.Count == 2 && node.Outgoing[0].Condition != null && node.Outgoing[1].Condition == null && node.Outgoing[0].Target != null && node.Outgoing[1].Target != null) { // Then check the structure of the tails. BlockNode node0 = graph[node.Outgoing[0].Target]; BlockNode node1 = graph[node.Outgoing[1].Target]; if (node0.Outgoing.Count == 1 && node1.Outgoing.Count == 1 && node0.Outgoing[0].Target == node1.Outgoing[0].Target && node0.Block.Statements != null && node1.Block.Statements != null && node0.Block.Statements.Count > 0 && node1.Block.Statements.Count > 0 && (node0.Block.Statements.Count < 2 || node0.Block.Statements[1] is Branch) && (node1.Block.Statements.Count < 2 || node1.Block.Statements[1] is Branch)) { // The structure matches, so analyze the conditions. Expression condition = node.Outgoing[0].Condition; ExpressionStatement trueStatement = node0.Block.Statements.Count > 0 ? node0.Block.Statements[0] as ExpressionStatement : null; ExpressionStatement falseStatement = node1.Block.Statements.Count > 0 ? node1.Block.Statements[0] as ExpressionStatement : null; Expression trueExpression = trueStatement != null ? trueStatement.Expression : null; Expression falseExpression = falseStatement != null ? falseStatement.Expression : null; if (trueExpression != null && trueExpression.Type != null && falseExpression != null && falseExpression.Type != null) { // Fix up the type information on boolean operators. NodeType[] booleanNodeTypes = { NodeType.Ceq, NodeType.Cgt, NodeType.Clt, NodeType.Cgt_Un, NodeType.Clt_Un, NodeType.Eq, NodeType.Gt, NodeType.Ge, NodeType.Lt, NodeType.Le, NodeType.LogicalAnd, NodeType.LogicalOr, NodeType.LogicalEqual, NodeType.LogicalImply, NodeType.LogicalNot }; if (Array.IndexOf(booleanNodeTypes, condition.NodeType) >= 0) { condition.Type = SystemTypes.Boolean; } if (Array.IndexOf(booleanNodeTypes, trueExpression.NodeType) >= 0) { trueExpression.Type = SystemTypes.Boolean; } if (Array.IndexOf(booleanNodeTypes, falseExpression.NodeType) >= 0) { falseExpression.Type = SystemTypes.Boolean; } // The expressions are valid, so see what kind of abnormal expression to create. Expression abnormal = null; // Check for short-circuit boolean binary expressions. Literal literal = trueExpression as Literal; if (literal != null && falseExpression.Type == SystemTypes.Boolean) { if (0.Equals(literal.Value)) // !trueExp && falseExp { abnormal = new BinaryExpression( Abnormalizer.LogicallyNegate(condition), falseExpression, NodeType.LogicalAnd, SystemTypes.Boolean, condition.SourceContext); } else if (1.Equals(literal.Value)) // trueExp || falseExp { abnormal = new BinaryExpression( condition, falseExpression, NodeType.LogicalOr, SystemTypes.Boolean, condition.SourceContext); } } // Possibly merge it as part of an existing ternary expression. if (abnormal == null) { TernaryExpression ternary = falseExpression as TernaryExpression; if (ternary != null && ternary.NodeType == NodeType.Conditional) { if (trueExpression == ternary.Operand3) { // It's a logical && embedded in a ternary expression. abnormal = new TernaryExpression( new BinaryExpression( Abnormalizer.LogicallyNegate(condition), ternary.Operand1, NodeType.LogicalAnd, SystemTypes.Boolean, condition.SourceContext), ternary.Operand2, ternary.Operand3, ternary.NodeType, ternary.Type); } else if (trueExpression == ternary.Operand2) { // It's a logical || embedded in a ternary expression. abnormal = new TernaryExpression( new BinaryExpression( condition, ternary.Operand1, NodeType.LogicalOr, SystemTypes.Boolean, condition.SourceContext), ternary.Operand2, ternary.Operand3, ternary.NodeType, ternary.Type); } } } // If all else fails, treat it as a ternary expression. if (abnormal == null) { // Infer the correct type of the expression. TypeNode type = null; if (Literal.IsNullLiteral(trueExpression)) { type = falseExpression.Type; } else if (Literal.IsNullLiteral(falseExpression)) { type = trueExpression.Type; } else if (trueExpression.Type.IsAssignableTo(falseExpression.Type)) { type = falseExpression.Type; } else if (falseExpression.Type.IsAssignableTo(trueExpression.Type)) { type = trueExpression.Type; } else // Instead of finding the greatest common denominator, just use object. { type = SystemTypes.Object; } abnormal = new TernaryExpression( condition, trueExpression, falseExpression, NodeType.Conditional, type); } Debug.Assert(abnormal.Type != null, "We should never create an expression with a null type."); // Try and get the best source context for the abnormal expression. if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = node.Outgoing[0].SourceContext; } if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = condition.SourceContext; } if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = trueExpression.SourceContext; } if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = falseExpression.SourceContext; } if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = trueStatement.SourceContext; } if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = falseStatement.SourceContext; } if (abnormal.SourceContext.Document == null) { abnormal.SourceContext = node.Block.SourceContext; } // TODO: Keep looking more places for the elusive source context. // Replace the branch with the abnormal expression and unconditional jump. RemoveStatements(node.Block.Statements, node.Outgoing[0]); node.Block.Statements.Add(new ExpressionStatement(abnormal, abnormal.SourceContext)); node.Block.Statements.Add(node0.Outgoing[0]); node.Refresh(); // Remove the tail blocks if they are no longer used. if (node0.Incoming.Count == 0) { graph.Remove(node0); } if (node1.Incoming.Count == 0) { graph.Remove(node1); } return(true); } } } return(false); }