Browse Source

Add support for checked/unchecked statements.

pull/100/head
Daniel Grunwald 14 years ago
parent
commit
942131b06d
  1. 38
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 296
      ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
  3. 1
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  4. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 32
      ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs
  6. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

38
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -221,43 +221,55 @@ namespace ICSharpCode.Decompiler.Ast @@ -221,43 +221,55 @@ namespace ICSharpCode.Decompiler.Ast
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
Ast.Expression arg3 = args.Count >= 3 ? args[2] : null;
switch(byteCode.Code) {
switch (byteCode.Code) {
#region Arithmetic
case ILCode.Add:
case ILCode.Add_Ovf:
case ILCode.Add_Ovf_Un:
{
BinaryOperatorExpression boe;
if (byteCode.InferredType is PointerType) {
if (byteCode.Arguments[0].ExpectedType is PointerType) {
arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType);
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2)
.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
} else if (byteCode.Arguments[1].ExpectedType is PointerType) {
arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType);
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2)
.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
} else {
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
}
} else {
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
}
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
boe.AddAnnotation(byteCode.Code == ILCode.Add ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
return boe;
}
case ILCode.Sub:
case ILCode.Sub_Ovf:
case ILCode.Sub_Ovf_Un:
{
BinaryOperatorExpression boe;
if (byteCode.InferredType is PointerType) {
if (byteCode.Arguments[0].ExpectedType is PointerType) {
arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType);
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2)
.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
} else {
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
}
} else {
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
}
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
boe.AddAnnotation(byteCode.Code == ILCode.Sub ? AddCheckedBlocks.UncheckedAnnotation : AddCheckedBlocks.CheckedAnnotation);
return boe;
}
case ILCode.Div: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
case ILCode.Div_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Divide, arg2);
case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2);
case ILCode.Mul: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
case ILCode.Mul_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation);
case ILCode.Mul_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Multiply, arg2).WithAnnotation(AddCheckedBlocks.CheckedAnnotation);
case ILCode.Rem: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
case ILCode.Rem_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Modulus, arg2);
case ILCode.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2);
@ -266,7 +278,7 @@ namespace ICSharpCode.Decompiler.Ast @@ -266,7 +278,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2);
case ILCode.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Shr_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1);
case ILCode.Neg: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Minus, arg1).WithAnnotation(AddCheckedBlocks.UncheckedAnnotation);
case ILCode.Not: return new Ast.UnaryOperatorExpression(UnaryOperatorType.BitNot, arg1);
#endregion
#region Arrays

296
ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs

@ -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;
}
}
}

1
ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -22,6 +22,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -22,6 +22,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new PatternStatementTransform(context),
new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(),
new AddCheckedBlocks(),
new DeclareVariables(context), // should run after most transforms that modify statements
new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables
new IntroduceUsingDeclarations(context)

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -57,6 +57,7 @@ @@ -57,6 +57,7 @@
<Compile Include="Ast\NameVariables.cs" />
<Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\AddCheckedBlocks.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" />

32
ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs

@ -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;
}
}
}
}
}

1
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -53,6 +53,7 @@ @@ -53,6 +53,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="Switch.cs" />
<Compile Include="UnsafeCode.cs" />
<Compile Include="YieldReturn.cs" />

Loading…
Cancel
Save