diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
index bd7b1173f..1b77c7441 100644
--- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
+++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
@@ -236,43 +236,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);
@@ -281,7 +293,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
@@ -372,7 +384,14 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Conv_U8:
case ILCode.Conv_I:
case ILCode.Conv_U:
- return arg1; // conversion is handled by Convert() function using the info from type analysis
+ {
+ // conversion was handled by Convert() function using the info from type analysis
+ CastExpression cast = arg1 as CastExpression;
+ if (cast != null) {
+ cast.AddAnnotation(AddCheckedBlocks.UncheckedAnnotation);
+ }
+ return arg1;
+ }
case ILCode.Conv_R4: return arg1.CastTo(typeof(float));
case ILCode.Conv_R8: return arg1.CastTo(typeof(double));
case ILCode.Conv_R_Un: return arg1.CastTo(typeof(double)); // TODO
@@ -392,7 +411,14 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Conv_Ovf_U2_Un:
case ILCode.Conv_Ovf_U4_Un:
case ILCode.Conv_Ovf_U8_Un:
- return arg1; // conversion was handled by Convert() function using the info from type analysis
+ {
+ // conversion was handled by Convert() function using the info from type analysis
+ CastExpression cast = arg1 as CastExpression;
+ if (cast != null) {
+ cast.AddAnnotation(AddCheckedBlocks.CheckedAnnotation);
+ }
+ return arg1;
+ }
case ILCode.Conv_Ovf_I: return arg1.CastTo(typeof(IntPtr)); // TODO
case ILCode.Conv_Ovf_U: return arg1.CastTo(typeof(UIntPtr));
case ILCode.Conv_Ovf_I_Un: return arg1.CastTo(typeof(IntPtr));
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
new file mode 100644
index 000000000..e598949c0
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
@@ -0,0 +1,301 @@
+// 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
+{
+ ///
+ /// Add checked/unchecked blocks.
+ ///
+ public class AddCheckedBlocks : IAstTransform
+ {
+ #region Annotation
+ sealed class CheckedUncheckedAnnotation {
+ ///
+ /// true=checked, false=unchecked
+ ///
+ 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
+ ///
+ /// Holds the blocks and expressions that should be inserted
+ ///
+ 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; // 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.NextStatement; stmt != lastStatement; stmt = next) {
+ next = stmt.NextStatement;
+ 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
+ ///
+ /// Holds the result of an insertion operation.
+ ///
+ 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();
+ 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;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
index be3998c6c..4c7f3bb1a 100644
--- a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
+++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
@@ -24,10 +24,6 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void Run(AstNode compilationUnit)
{
- // Don't show using when decompiling a single method or nested types:
- if (context.CurrentMethod != null || (context.CurrentType != null && context.CurrentType.DeclaringType != null))
- return;
-
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null);
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
index 6e2446559..187086058 100644
--- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
+++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
@@ -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)
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 2bdf32b86..8a5716ec1 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -58,6 +58,7 @@
+
diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
index 034d93722..a1c43556b 100644
--- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
+++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
@@ -38,6 +38,7 @@ namespace ICSharpCode.Decompiler.ILAst
InlineVariables3,
CachedDelegateInitialization,
IntroduceFixedStatements,
+ RecombineVariables,
TypeInference2,
RemoveRedundantCode3,
None
@@ -106,6 +107,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return;
modified |= block.RunOptimization(TransformDecimalCtorToConstant);
+ modified |= block.RunOptimization(SimplifyLdcI4ConvI8);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
@@ -180,6 +182,9 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
+ if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) return;
+ RecombineVariables(method);
+
if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return;
TypeAnalysis.Reset(method);
TypeAnalysis.Run(context, method);
@@ -458,6 +463,25 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
+ void RecombineVariables(ILBlock method)
+ {
+ // Recombine variables that were split when the ILAst was created
+ // This ensures that a single IL variable is a single C# variable (gets assigned only one name)
+ // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes.
+ Dictionary dict = new Dictionary();
+ foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) {
+ ILVariable v = expr.Operand as ILVariable;
+ if (v != null && v.OriginalVariable != null) {
+ ILVariable combinedVariable;
+ if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) {
+ dict.Add(v.OriginalVariable, v);
+ combinedVariable = v;
+ }
+ expr.Operand = combinedVariable;
+ }
+ }
+ }
+
void ReportUnassignedILRanges(ILBlock method)
{
var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList();
@@ -511,7 +535,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
///
- /// The expression has no effect on the program and can be removed
+ /// The expression has no effect on the program and can be removed
/// if its return value is not needed.
///
public static bool HasNoSideEffects(this ILExpression expr)
diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
index da9908e45..8c15dffcf 100644
--- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
+++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
@@ -13,6 +13,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
public partial class ILAstOptimizer
{
+ #region TransformDecimalCtorToConstant
static bool TransformDecimalCtorToConstant(List body, ILExpression expr, int pos)
{
MethodReference r;
@@ -52,7 +53,9 @@ namespace ICSharpCode.Decompiler.ILAst
}
return modified;
}
+ #endregion
+ #region SimplifyLdObjAndStObj
static bool SimplifyLdObjAndStObj(List body, ILExpression expr, int pos)
{
bool modified = false;
@@ -66,17 +69,17 @@ namespace ICSharpCode.Decompiler.ILAst
ILCode? newCode = null;
if (expr.Match(ILCode.Stobj, out type, out arg, out arg2)) {
switch (arg.Code) {
- case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break;
- case ILCode.Ldloca: newCode = ILCode.Stloc; break;
- case ILCode.Ldflda: newCode = ILCode.Stfld; break;
- case ILCode.Ldsflda: newCode = ILCode.Stsfld; break;
+ case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break;
+ case ILCode.Ldloca: newCode = ILCode.Stloc; break;
+ case ILCode.Ldflda: newCode = ILCode.Stfld; break;
+ case ILCode.Ldsflda: newCode = ILCode.Stsfld; break;
}
} else if (expr.Match(ILCode.Ldobj, out type, out arg)) {
switch (arg.Code) {
- case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break;
- case ILCode.Ldloca: newCode = ILCode.Ldloc; break;
- case ILCode.Ldflda: newCode = ILCode.Ldfld; break;
- case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break;
+ case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break;
+ case ILCode.Ldloca: newCode = ILCode.Ldloc; break;
+ case ILCode.Ldflda: newCode = ILCode.Ldfld; break;
+ case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break;
}
}
if (newCode != null) {
@@ -89,6 +92,26 @@ namespace ICSharpCode.Decompiler.ILAst
}
return modified;
}
+ #endregion
+
+ #region SimplifyLdcI4ConvI8
+ static bool SimplifyLdcI4ConvI8(List body, ILExpression expr, int pos)
+ {
+ ILExpression ldc;
+ int val;
+ if (expr.Match(ILCode.Conv_I8, out ldc) && ldc.Match(ILCode.Ldc_I4, out val)) {
+ expr.Code = ILCode.Ldc_I8;
+ expr.Operand = (long)val;
+ expr.Arguments.Clear();
+ return true;
+ }
+ bool modified = false;
+ foreach(ILExpression arg in expr.Arguments) {
+ modified |= SimplifyLdcI4ConvI8(null, arg, -1);
+ }
+ return modified;
+ }
+ #endregion
#region CachedDelegateInitialization
void CachedDelegateInitialization(ILBlock block, ref int i)
diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
index 768257c06..7eca47d99 100644
--- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
+++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
@@ -689,7 +689,9 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (targetBitSize >= NativeInt && argType is PointerType) {
return argType;
}
- return (GetInformationAmount(expectedType) == targetBitSize && IsSigned(expectedType) == targetSigned) ? expectedType : targetType;
+ TypeReference resultType = (GetInformationAmount(expectedType) == targetBitSize && IsSigned(expectedType) == targetSigned) ? expectedType : targetType;
+ arg.ExpectedType = resultType; // store the expected type in the argument so that AstMethodBodyBuilder will insert a cast
+ return resultType;
}
static TypeReference GetFieldType(FieldReference fieldReference)
diff --git a/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs b/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs
new file mode 100644
index 000000000..21533d69e
--- /dev/null
+++ b/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs
@@ -0,0 +1,40 @@
+// 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 Cast(int a)
+ {
+ short c1 = checked((short)a);
+ short u1 = unchecked((short)a);
+ byte c2 = checked((byte)a);
+ byte u2 = unchecked((byte)a);
+ }
+
+ public void ForWithCheckedIteratorAndUncheckedBody(int n)
+ {
+ checked {
+ for (int i = n + 1; i < n + 1; i++) {
+ unchecked {
+ n = i * i;
+ }
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
index 70b2eb55c..2e88a92dc 100644
--- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -53,6 +53,7 @@
+