From 62fe21fb9f67736e462da4189840299fa77702d2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 11 Jun 2015 23:43:55 +0200 Subject: [PATCH] add transformation of decimal literals and decimal constants --- .../CSharp/CSharpDecompiler.cs | 2 +- .../CSharp/ExpressionBuilder.cs | 7 +++ .../Transforms/DecimalConstantTransform.cs | 19 ++++---- .../ICSharpCode.Decompiler.csproj | 1 + ICSharpCode.Decompiler/IL/Instructions.cs | 46 +++++++++++++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 2 + .../IL/Transforms/ExpressionTransforms.cs | 45 +++++++++++++++++- .../Tests/ICSharpCode.Decompiler.Tests.csproj | 1 + .../Tests/TestCases/DecimalFields.cs | 42 +++++++++++++++++ ICSharpCode.Decompiler/Tests/TestRunner.cs | 6 +++ 10 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 ICSharpCode.Decompiler/Tests/TestCases/DecimalFields.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 5b63fadf6..d2ea0aec9 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp new AddCheckedBlocks(), //new DeclareVariables(context), // should run after most transforms that modify statements new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables - //new DecimalConstantTransform(), + new DecimalConstantTransform(), new IntroduceUsingDeclarations(), //new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations //new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 3106ce04a..5928e37ef 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -177,6 +177,13 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Double), inst.Value)); } + protected internal override TranslatedExpression VisitLdcDecimal(LdcDecimal inst) + { + return new PrimitiveExpression(inst.Value) + .WithILInstruction(inst) + .WithRR(new ConstantResolveResult(compilation.FindType(KnownTypeCode.Decimal), inst.Value)); + } + protected internal override TranslatedExpression VisitLdStr(LdStr inst) { return new PrimitiveExpression(inst.Value) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DecimalConstantTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DecimalConstantTransform.cs index acafe2c6f..b67d42ed3 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DecimalConstantTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DecimalConstantTransform.cs @@ -19,40 +19,39 @@ using System; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.PatternMatching; -using Mono.Cecil; +using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.Decompiler.CSharp.Transforms { /// /// Transforms decimal constant fields. /// - public class DecimalConstantTransform : DepthFirstAstVisitor, IAstTransform + public class DecimalConstantTransform : DepthFirstAstVisitor, IAstTransform { static readonly PrimitiveType decimalType = new PrimitiveType("decimal"); - public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data) + public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly; if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) { foreach (var attributeSection in fieldDeclaration.Attributes) { foreach (var attribute in attributeSection.Attributes) { - TypeReference tr = attribute.Type.Annotation(); - if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") { + var t = attribute.Type.GetSymbol() as IType; + if (t != null && t.Name == "DecimalConstantAttribute" && t.Namespace == "System.Runtime.CompilerServices") { attribute.Remove(); if (attributeSection.Attributes.Count == 0) attributeSection.Remove(); fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const; - return null; + return; } } } } - return null; } - - public void Run(AstNode compilationUnit) + + public void Run(AstNode rootNode, TransformContext context) { - compilationUnit.AcceptVisitor(this, null); + rootNode.AcceptVisitor(this); } } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 4e37497c1..0ad496409 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -64,6 +64,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index b10712743..3a927e95f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -113,6 +113,8 @@ namespace ICSharpCode.Decompiler.IL LdcI8, /// Loads a constant floating-point number. LdcF, + /// Loads a constant decimal. + LdcDecimal, /// Loads the null reference. LdNull, /// Load method pointer @@ -1539,6 +1541,31 @@ namespace ICSharpCode.Decompiler.IL } } + /// Loads a constant decimal. + public sealed partial class LdcDecimal : SimpleInstruction + { + public LdcDecimal(decimal value) : base(OpCode.LdcDecimal) + { + this.Value = value; + } + public readonly decimal Value; + public override StackType ResultType { get { return StackType.O; } } + public override void WriteTo(ITextOutput output) + { + output.Write(OpCode); + output.Write(' '); + Disassembler.DisassemblerHelpers.WriteOperand(output, Value); + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitLdcDecimal(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitLdcDecimal(this); + } + } + /// Loads the null reference. public sealed partial class LdNull : SimpleInstruction { @@ -3163,6 +3190,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitLdcDecimal(LdcDecimal inst) + { + Default(inst); + } protected internal virtual void VisitLdNull(LdNull inst) { Default(inst); @@ -3473,6 +3504,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitLdcDecimal(LdcDecimal inst) + { + return Default(inst); + } protected internal virtual T VisitLdNull(LdNull inst) { return Default(inst); @@ -3704,6 +3739,7 @@ namespace ICSharpCode.Decompiler.IL "ldc.i4", "ldc.i8", "ldc.f", + "ldc.decimal", "ldnull", "ldftn", "ldvirtftn", @@ -4048,6 +4084,16 @@ namespace ICSharpCode.Decompiler.IL value = default(double); return false; } + public bool MatchLdcDecimal(out decimal value) + { + var inst = this as LdcDecimal; + if (inst != null) { + value = inst.Value; + return true; + } + value = default(decimal); + return false; + } public bool MatchLdNull() { var inst = this as LdNull; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 6c7b99a4f..84973c557 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -122,6 +122,8 @@ LoadConstant("long"), ResultType("I8")), new OpCode("ldc.f", "Loads a constant floating-point number.", LoadConstant("double"), ResultType("F")), + new OpCode("ldc.decimal", "Loads a constant decimal.", + LoadConstant("decimal"), ResultType("O")), new OpCode("ldnull", "Loads the null reference.", CustomClassName("LdNull"), NoArguments, ResultType("O")), new OpCode("ldftn", "Load method pointer", diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 0a86c0cca..19dc4702a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -74,8 +74,51 @@ namespace ICSharpCode.Decompiler.IL.Transforms var newObj = new NewObj(inst.Method); newObj.ILRange = inst.ILRange; newObj.Arguments.AddRange(inst.Arguments.Skip(1)); - var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType); + LdcDecimal decimalConstant; + ILInstruction result; + if (TransformDecimalCtorToConstant(newObj, out decimalConstant)) { + result = decimalConstant; + } else { + result = newObj; + } + var expr = new StObj(inst.Arguments[0], result, inst.Method.DeclaringType); inst.ReplaceWith(expr); } + + protected internal override void VisitNewObj(NewObj inst) + { + LdcDecimal decimalConstant; + if (TransformDecimalCtorToConstant(inst, out decimalConstant)) { + inst.ReplaceWith(decimalConstant); + return; + } + base.VisitNewObj(inst); + } + + bool TransformDecimalCtorToConstant(NewObj inst, out LdcDecimal result) + { + IType t = inst.Method.DeclaringType; + result = null; + if (!t.IsKnownType(KnownTypeCode.Decimal)) + return false; + var args = inst.Arguments; + if (args.Count == 1) { + int val; + if (args[0].MatchLdcI4(out val)) { + result = new LdcDecimal(val); + return true; + } + } else if (args.Count == 5) { + int lo, mid, hi, isNegative, scale; + if (args[0].MatchLdcI4(out lo) && args[1].MatchLdcI4(out mid) && + args[2].MatchLdcI4(out hi) && args[3].MatchLdcI4(out isNegative) && + args[4].MatchLdcI4(out scale)) + { + result = new LdcDecimal(new decimal(lo, mid, hi, isNegative != 0, (byte)scale)); + return true; + } + } + return false; + } } } diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 98dc8932f..1653933f1 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -104,6 +104,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/TestCases/DecimalFields.cs b/ICSharpCode.Decompiler/Tests/TestCases/DecimalFields.cs new file mode 100644 index 000000000..b683fffa1 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/TestCases/DecimalFields.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases +{ + /// + /// Description of DecimalFields. + /// + public class DecimalFields + { + public const decimal field1 = 1.5m; + public const decimal field2 = decimal.One; + public const decimal field3 = decimal.MaxValue; + public const decimal field4 = decimal.MinValue; + + public static int Main() + { + Console.WriteLine(field1); + Console.WriteLine(field2); + Console.WriteLine(field3); + Console.WriteLine(field4); + return 0; + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index f825a6006..25c6a9763 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -75,6 +75,12 @@ namespace ICSharpCode.Decompiler.Tests { TestCompileDecompileCompileOutputAll("InitializerTests.cs"); } + + [Test] + public void DecimalFields() + { + TestCompileDecompileCompileOutputAll("DecimalFields.cs"); + } void TestCompileDecompileCompileOutputAll(string testFileName) {