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 @@ +