mirror of https://github.com/icsharpcode/ILSpy.git
6 changed files with 356 additions and 13 deletions
@ -0,0 +1,296 @@
@@ -0,0 +1,296 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
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. Make the scope of checked blocks as large as possible |
||||
(where goal 1 has the highest priority) |
||||
*/ |
||||
|
||||
#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) |
||||
{ |
||||
this.Blocks = blocks; |
||||
this.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(AddCheckedBlocks.InsertedNode child1, AddCheckedBlocks.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 InsertedBlock : InsertedNode |
||||
{ |
||||
readonly Statement firstStatement; |
||||
readonly Statement lastStatement; |
||||
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(); |
||||
for (Statement stmt = firstStatement.NextStatement; stmt != lastStatement; stmt = stmt.NextStatement) { |
||||
newBlock.Add(stmt.Detach()); |
||||
} |
||||
if (isChecked) |
||||
firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock }); |
||||
else |
||||
firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock }); |
||||
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 4)
|
||||
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 early as possible. (goal 4)
|
||||
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.NextStatement; |
||||
} |
||||
|
||||
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, set the cost of the other context to infinite.
|
||||
if (annotation.IsChecked) |
||||
result.CostInUncheckedContext = Cost.Infinite; |
||||
else |
||||
result.CostInCheckedContext = Cost.Infinite; |
||||
} |
||||
// Embed this node in an checked/unchecked expression:
|
||||
// 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; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
|
||||
public class CheckedUnchecked |
||||
{ |
||||
public void Operators(int a, int b) |
||||
{ |
||||
int c1 = checked(a + b); |
||||
int u1 = unchecked(a + b); |
||||
int c2 = checked(a - b); |
||||
int u2 = unchecked(a - b); |
||||
int c3 = checked(a * b); |
||||
int u3 = unchecked(a * b); |
||||
int c4 = checked(a / b); |
||||
int u4 = unchecked(a / b); |
||||
int c5 = checked(a % b); |
||||
int u5 = unchecked(a % b); |
||||
} |
||||
|
||||
public void ForWithCheckedIteratorAndUncheckedBody(int n) |
||||
{ |
||||
checked { |
||||
for (int i = n + 1; i < n + 1; i++) { |
||||
unchecked { |
||||
n = i * i; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue