diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index fe571a353..de2694415 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -531,6 +531,7 @@ namespace Decompiler case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: + case (Code)ILCode.Ldc_Decimal: return new Ast.PrimitiveExpression(operand); case Code.Ldfld: if (arg1 is DirectionExpression) diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 4871d0f40..076fa0627 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -262,6 +262,7 @@ namespace Decompiler TernaryOp, // ?: LoopBreak, LoopContinue, + Ldc_Decimal, Pattern // used for ILAst pattern nodes } diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index d1fd94012..50d8ae898 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using ICSharpCode.NRefactory.Utils; +using Mono.Cecil; namespace Decompiler { @@ -16,29 +17,93 @@ namespace Decompiler { public static void Run(DecompilerContext context, ILBlock method) { - // TODO: move this somewhere else - // Eliminate 'dups': - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { - for (int i = 0; i < expr.Arguments.Count; i++) { - if (expr.Arguments[i].Code == ILCode.Dup) - expr.Arguments[i] = expr.Arguments[i].Arguments[0]; - } - } - - PeepholeTransform[] transforms = { + PeepholeTransform[] blockTransforms = { ArrayInitializers.Transform(method) }; + Func[] exprTransforms = { + EliminateDups, + HandleDecimalConstants + }; // Traverse in post order so that nested blocks are transformed first. This is required so that // patterns on the parent block can assume that all nested blocks are already transformed. - foreach (var block in TreeTraversal.PostOrder(method, c => c != null ? c.GetChildren() : null).OfType()) { - // go through the instructions in reverse so that - for (int i = block.Body.Count - 1; i >= 0; i--) { - context.CancellationToken.ThrowIfCancellationRequested(); - foreach (var t in transforms) { - t(block, ref i); + foreach (var node in TreeTraversal.PostOrder(method, c => c != null ? c.GetChildren() : null)) { + ILBlock block = node as ILBlock; + ILExpression expr; + if (block != null) { + // go through the instructions in reverse so that transforms can build up nested structures inside-out + for (int i = block.Body.Count - 1; i >= 0; i--) { + context.CancellationToken.ThrowIfCancellationRequested(); + expr = block.Body[i] as ILExpression; + if (expr != null) { + // apply expr transforms to top-level expr in block + foreach (var t in exprTransforms) + expr = t(expr); + block.Body[i] = expr; + } + // apply block transforms + foreach (var t in blockTransforms) { + t(block, ref i); + } + } + } + expr = node as ILExpression; + if (expr != null) { + // apply expr transforms to all arguments + for (int i = 0; i < expr.Arguments.Count; i++) { + ILExpression arg = expr.Arguments[i]; + foreach (var t in exprTransforms) + arg = t(arg); + expr.Arguments[i] = arg; + } + } + } + } + + static ILExpression EliminateDups(ILExpression expr) + { + if (expr.Code == ILCode.Dup) + return expr.Arguments.Single(); + else + return expr; + } + + static ILExpression HandleDecimalConstants(ILExpression expr) + { + if (expr.Code == ILCode.Newobj) { + MethodReference r = (MethodReference)expr.Operand; + if (r.DeclaringType.Name == "Decimal" && r.DeclaringType.Namespace == "System") { + if (expr.Arguments.Count == 1) { + int? val = GetI4Constant(expr.Arguments[0]); + if (val != null) { + expr.Arguments.Clear(); + expr.Code = ILCode.Ldc_Decimal; + expr.Operand = new decimal(val.Value); + expr.InferredType = r.DeclaringType; + } + } else if (expr.Arguments.Count == 5) { + int? lo = GetI4Constant(expr.Arguments[0]); + int? mid = GetI4Constant(expr.Arguments[1]); + int? hi = GetI4Constant(expr.Arguments[2]); + int? isNegative = GetI4Constant(expr.Arguments[3]); + int? scale = GetI4Constant(expr.Arguments[4]); + if (lo != null && mid != null && hi != null && isNegative != null && scale != null) { + expr.Arguments.Clear(); + expr.Code = ILCode.Ldc_Decimal; + expr.Operand = new decimal(lo.Value, mid.Value, hi.Value, isNegative.Value != 0, (byte)scale); + expr.InferredType = r.DeclaringType; + } } } } + return expr; + } + + static int? GetI4Constant(ILExpression expr) + { + if (expr != null && expr.Code == ILCode.Ldc_I4) + return (int)expr.Operand; + else + return null; } } } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 31082f7f9..8f234ba7b 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -303,6 +303,9 @@ namespace Decompiler return typeSystem.Single; case ILCode.Ldc_R8: return typeSystem.Double; + case ILCode.Ldc_Decimal: + Debug.Assert(expr.InferredType != null && expr.InferredType.FullName == "System.Decimal"); + return expr.InferredType; case ILCode.Ldtoken: if (expr.Operand is TypeReference) return new TypeReference("System", "RuntimeTypeHandle", module, module, true);