diff options
Diffstat (limited to 'ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs')
-rw-r--r-- | ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs | 368 |
1 files changed, 0 insertions, 368 deletions
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs deleted file mode 100644 index dc018eb1..00000000 --- a/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Linq; -using ICSharpCode.Decompiler.ILAst; -using ICSharpCode.NRefactory.CSharp; - -namespace ICSharpCode.Decompiler.Ast.Transforms -{ - /// <summary> - /// Add checked/unchecked blocks. - /// </summary> - public class AddCheckedBlocks : IAstTransform - { - #region Annotation - sealed class CheckedUncheckedAnnotation { - /// <summary> - /// true=checked, false=unchecked - /// </summary> - public bool IsChecked; - } - - public static readonly object CheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = true }; - public static readonly object UncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false }; - #endregion - - /* - We treat placing checked/unchecked blocks as an optimization problem, with the following goals: - 1. Use minimum number of checked blocks+expressions - 2. Prefer checked expressions over checked blocks - 3. Make the scope of checked expressions as small as possible - 4. Open checked blocks as late as possible, and close checked blocks as late as possible - (where goal 1 has the highest priority) - - Goal 4a (open checked blocks as late as possible) is necessary so that we don't move variable declarations - into checked blocks, as the variable might still be used after the checked block. - (this could cause DeclareVariables to omit the variable declaration, producing incorrect code) - Goal 4b (close checked blocks as late as possible) makes the code look nicer in this case: - checked { - int c = a + b; - int r = a + c; - return r; - } - If the checked block was closed as early as possible, the variable r would have to be declared outside - (this would work, but look badly) - */ - - #region struct Cost - struct Cost - { - // highest possible cost so that the Blocks+Expressions addition doesn't overflow - public static readonly Cost Infinite = new Cost(0x3fffffff, 0x3fffffff); - - public readonly int Blocks; - public readonly int Expressions; - - public Cost(int blocks, int expressions) - { - Blocks = blocks; - Expressions = expressions; - } - - public static bool operator <(Cost a, Cost b) - { - return a.Blocks + a.Expressions < b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks < b.Blocks; - } - - public static bool operator >(Cost a, Cost b) - { - return a.Blocks + a.Expressions > b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks > b.Blocks; - } - - public static bool operator <=(Cost a, Cost b) - { - return a.Blocks + a.Expressions < b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks <= b.Blocks; - } - - public static bool operator >=(Cost a, Cost b) - { - return a.Blocks + a.Expressions > b.Blocks + b.Expressions - || a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks >= b.Blocks; - } - - public static Cost operator +(Cost a, Cost b) - { - return new Cost(a.Blocks + b.Blocks, a.Expressions + b.Expressions); - } - - public override string ToString() - { - return string.Format("[{0} + {1}]", Blocks, Expressions); - } - } - #endregion - - #region class InsertedNode - /// <summary> - /// Holds the blocks and expressions that should be inserted - /// </summary> - abstract class InsertedNode - { - public static InsertedNode operator +(InsertedNode a, InsertedNode b) - { - if (a == null) - return b; - if (b == null) - return a; - return new InsertedNodeList(a, b); - } - - public abstract void Insert(); - } - - class InsertedNodeList : InsertedNode - { - readonly InsertedNode child1, child2; - - public InsertedNodeList(InsertedNode child1, InsertedNode child2) - { - this.child1 = child1; - this.child2 = child2; - } - - public override void Insert() - { - child1.Insert(); - child2.Insert(); - } - } - - class InsertedExpression : InsertedNode - { - readonly Expression expression; - readonly bool isChecked; - - public InsertedExpression(Expression expression, bool isChecked) - { - this.expression = expression; - this.isChecked = isChecked; - } - - public override void Insert() - { - if (isChecked) - expression.ReplaceWith(e => new CheckedExpression { Expression = e }); - else - expression.ReplaceWith(e => new UncheckedExpression { Expression = e }); - } - } - - class ConvertCompoundAssignment : InsertedNode - { - readonly Expression expression; - readonly bool isChecked; - - public ConvertCompoundAssignment(Expression expression, bool isChecked) - { - this.expression = expression; - this.isChecked = isChecked; - } - - public override void Insert() - { - AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression); - expression.ReplaceWith(assign); - if (isChecked) - assign.Right = new CheckedExpression { Expression = assign.Right.Detach() }; - else - assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() }; - } - } - - class InsertedBlock : InsertedNode - { - readonly Statement firstStatement; // inclusive - readonly Statement lastStatement; // exclusive - readonly bool isChecked; - - public InsertedBlock(Statement firstStatement, Statement lastStatement, bool isChecked) - { - this.firstStatement = firstStatement; - this.lastStatement = lastStatement; - this.isChecked = isChecked; - } - - public override void Insert() - { - BlockStatement newBlock = new BlockStatement(); - // Move all statements except for the first - Statement next; - for (Statement stmt = firstStatement.GetNextStatement(); stmt != lastStatement; stmt = next) { - next = stmt.GetNextStatement(); - newBlock.Add(stmt.Detach()); - } - // Replace the first statement with the new (un)checked block - if (isChecked) - firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock }); - else - firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock }); - // now also move the first node into the new block - newBlock.Statements.InsertAfter(null, firstStatement); - } - } - #endregion - - #region class Result - /// <summary> - /// Holds the result of an insertion operation. - /// </summary> - class Result - { - public Cost CostInCheckedContext; - public InsertedNode NodesToInsertInCheckedContext; - public Cost CostInUncheckedContext; - public InsertedNode NodesToInsertInUncheckedContext; - } - #endregion - - public void Run(AstNode node) - { - BlockStatement block = node as BlockStatement; - if (block == null) { - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - Run(child); - } - } else { - Result r = GetResultFromBlock(block); - if (r.NodesToInsertInUncheckedContext != null) - r.NodesToInsertInUncheckedContext.Insert(); - } - } - - Result GetResultFromBlock(BlockStatement block) - { - // For a block, we are tracking 4 possibilities: - // a) context is checked, no unchecked block open - Cost costCheckedContext = new Cost(0, 0); - InsertedNode nodesCheckedContext = null; - // b) context is checked, an unchecked block is open - Cost costCheckedContextUncheckedBlockOpen = Cost.Infinite; - InsertedNode nodesCheckedContextUncheckedBlockOpen = null; - Statement uncheckedBlockStart = null; - // c) context is unchecked, no checked block open - Cost costUncheckedContext = new Cost(0, 0); - InsertedNode nodesUncheckedContext = null; - // d) context is unchecked, a checked block is open - Cost costUncheckedContextCheckedBlockOpen = Cost.Infinite; - InsertedNode nodesUncheckedContextCheckedBlockOpen = null; - Statement checkedBlockStart = null; - - Statement statement = block.Statements.FirstOrDefault(); - while (true) { - // Blocks can be closed 'for free'. We use '<=' so that blocks are closed as late as possible (goal 4b) - if (costCheckedContextUncheckedBlockOpen <= costCheckedContext) { - costCheckedContext = costCheckedContextUncheckedBlockOpen; - nodesCheckedContext = nodesCheckedContextUncheckedBlockOpen + new InsertedBlock(uncheckedBlockStart, statement, false); - } - if (costUncheckedContextCheckedBlockOpen <= costUncheckedContext) { - costUncheckedContext = costUncheckedContextCheckedBlockOpen; - nodesUncheckedContext = nodesUncheckedContextCheckedBlockOpen + new InsertedBlock(checkedBlockStart, statement, true); - } - if (statement == null) - break; - // Now try opening blocks. We use '<=' so that blocks are opened as late as possible. (goal 4a) - if (costCheckedContext + new Cost(1, 0) <= costCheckedContextUncheckedBlockOpen) { - costCheckedContextUncheckedBlockOpen = costCheckedContext + new Cost(1, 0); - nodesCheckedContextUncheckedBlockOpen = nodesCheckedContext; - uncheckedBlockStart = statement; - } - if (costUncheckedContext + new Cost(1, 0) <= costUncheckedContextCheckedBlockOpen) { - costUncheckedContextCheckedBlockOpen = costUncheckedContext + new Cost(1, 0); - nodesUncheckedContextCheckedBlockOpen = nodesUncheckedContext; - checkedBlockStart = statement; - } - // Now handle the statement - Result stmtResult = GetResult(statement); - - costCheckedContext += stmtResult.CostInCheckedContext; - nodesCheckedContext += stmtResult.NodesToInsertInCheckedContext; - costCheckedContextUncheckedBlockOpen += stmtResult.CostInUncheckedContext; - nodesCheckedContextUncheckedBlockOpen += stmtResult.NodesToInsertInUncheckedContext; - costUncheckedContext += stmtResult.CostInUncheckedContext; - nodesUncheckedContext += stmtResult.NodesToInsertInUncheckedContext; - costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext; - nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext; - - statement = statement.GetNextStatement(); - } - - return new Result { - CostInCheckedContext = costCheckedContext, NodesToInsertInCheckedContext = nodesCheckedContext, - CostInUncheckedContext = costUncheckedContext, NodesToInsertInUncheckedContext = nodesUncheckedContext - }; - } - - Result GetResult(AstNode node) - { - if (node is BlockStatement) - return GetResultFromBlock((BlockStatement)node); - Result result = new Result(); - for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { - Result childResult = GetResult(child); - result.CostInCheckedContext += childResult.CostInCheckedContext; - result.NodesToInsertInCheckedContext += childResult.NodesToInsertInCheckedContext; - result.CostInUncheckedContext += childResult.CostInUncheckedContext; - result.NodesToInsertInUncheckedContext += childResult.NodesToInsertInUncheckedContext; - } - Expression expr = node as Expression; - if (expr != null) { - CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>(); - if (annotation != null) { - // If the annotation requires this node to be in a specific context, add a huge cost to the other context - // That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any - // solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}") - if (annotation.IsChecked) - result.CostInUncheckedContext += new Cost(10000, 0); - else - result.CostInCheckedContext += new Cost(10000, 0); - } - // Embed this node in an checked/unchecked expression: - if (expr.Parent is ExpressionStatement) { - // We cannot use checked/unchecked for top-level-expressions. - // However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);) - // back to its old form. - if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) { - // We use '<' so that expressions are introduced on the deepest level possible (goal 3) - if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) { - result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1); - result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true); - } else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) { - result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1); - result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false); - } - } - } else if (expr.Role.IsValid(Expression.Null)) { - // We use '<' so that expressions are introduced on the deepest level possible (goal 3) - if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) { - result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1); - result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true); - } else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) { - result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1); - result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false); - } - } - } - return result; - } - } -} |