diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs index cc0887987..f32d2fb6b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs @@ -51,12 +51,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine(InlineAssignmentTest.field2 = new InlineAssignmentTest()); Console.WriteLine(InlineAssignmentTest.field2); this.UseShort(this.field4 = 6); + this.UseShort(this.field4 = -10000); + this.UseShort(this.field4 = (short)this.field1); + this.UseShort(this.field4 = this.UseShort(0)); Console.WriteLine(this.field4); } - public void UseShort(short s) + public short UseShort(short s) { Console.WriteLine(s); + return s; } public void ReadLoop1(TextReader r) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.il index ec2d0ea7e..9a1797896 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly uq03kalt +.assembly w1xqybg1 { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module uq03kalt.dll -// MVID: {D636DE2E-D2CD-4EDF-B87B-0B0985C6C0B8} +.module w1xqybg1.dll +// MVID: {1BE5829A-0D5E-43DC-8C02-E3012B1780A2} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x018D0000 +// Image base: 0x033B0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -93,7 +93,7 @@ .method public hidebysig instance void SimpleInlineWithFields2() cil managed { - // Code size 87 (0x57) + // Code size 154 (0x9a) .maxstack 4 .locals init (int32 V_0, int16 V_1) @@ -125,25 +125,62 @@ IL_003d: stloc.1 IL_003e: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 IL_0043: ldloc.1 - IL_0044: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) - IL_0049: nop + IL_0044: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_0049: pop IL_004a: ldarg.0 - IL_004b: ldfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 - IL_0050: call void [mscorlib]System.Console::WriteLine(int32) - IL_0055: nop - IL_0056: ret + IL_004b: ldarg.0 + IL_004c: ldc.i4 0xffffd8f0 + IL_0051: dup + IL_0052: stloc.1 + IL_0053: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_0058: ldloc.1 + IL_0059: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_005e: pop + IL_005f: ldarg.0 + IL_0060: ldarg.0 + IL_0061: ldarg.0 + IL_0062: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field1 + IL_0067: conv.i2 + IL_0068: dup + IL_0069: stloc.1 + IL_006a: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_006f: ldloc.1 + IL_0070: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_0075: pop + IL_0076: ldarg.0 + IL_0077: ldarg.0 + IL_0078: ldarg.0 + IL_0079: ldc.i4.0 + IL_007a: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_007f: dup + IL_0080: stloc.1 + IL_0081: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_0086: ldloc.1 + IL_0087: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_008c: pop + IL_008d: ldarg.0 + IL_008e: ldfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_0093: call void [mscorlib]System.Console::WriteLine(int32) + IL_0098: nop + IL_0099: ret } // end of method InlineAssignmentTest::SimpleInlineWithFields2 - .method public hidebysig instance void + .method public hidebysig instance int16 UseShort(int16 s) cil managed { - // Code size 9 (0x9) - .maxstack 8 + // Code size 14 (0xe) + .maxstack 1 + .locals init (int16 V_0) IL_0000: nop IL_0001: ldarg.1 IL_0002: call void [mscorlib]System.Console::WriteLine(int32) IL_0007: nop - IL_0008: ret + IL_0008: ldarg.1 + IL_0009: stloc.0 + IL_000a: br.s IL_000c + + IL_000c: ldloc.0 + IL_000d: ret } // end of method InlineAssignmentTest::UseShort .method public hidebysig instance void diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.opt.il index f98d18c87..2f944dee2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.opt.il @@ -10,7 +10,7 @@ .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } -.assembly zztqnmi4 +.assembly f1ibwsev { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -20,15 +20,15 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 } -.module zztqnmi4.dll -// MVID: {038F2DF5-AE7B-4B0A-B846-AC254CE46038} +.module f1ibwsev.dll +// MVID: {8DB4E6AA-A0C2-49F3-8E9C-A28CE30F145A} .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x10000000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x006A0000 +// Image base: 0x01400000 // =============== CLASS MEMBERS DECLARATION =================== @@ -85,10 +85,13 @@ .method public hidebysig instance void SimpleInlineWithFields2() cil managed { - // Code size 80 (0x50) + // Code size 150 (0x96) .maxstack 4 .locals init (int32 V_0, - int16 V_1) + int16 V_1, + int16 V_2, + int16 V_3, + int16 V_4) IL_0000: ldarg.0 IL_0001: ldc.i4.5 IL_0002: dup @@ -112,21 +115,54 @@ IL_0038: stloc.1 IL_0039: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 IL_003e: ldloc.1 - IL_003f: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) - IL_0044: ldarg.0 - IL_0045: ldfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 - IL_004a: call void [mscorlib]System.Console::WriteLine(int32) - IL_004f: ret + IL_003f: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_0044: pop + IL_0045: ldarg.0 + IL_0046: ldarg.0 + IL_0047: ldc.i4 0xffffd8f0 + IL_004c: dup + IL_004d: stloc.2 + IL_004e: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_0053: ldloc.2 + IL_0054: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_0059: pop + IL_005a: ldarg.0 + IL_005b: ldarg.0 + IL_005c: ldarg.0 + IL_005d: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field1 + IL_0062: conv.i2 + IL_0063: dup + IL_0064: stloc.3 + IL_0065: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_006a: ldloc.3 + IL_006b: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_0070: pop + IL_0071: ldarg.0 + IL_0072: ldarg.0 + IL_0073: ldarg.0 + IL_0074: ldc.i4.0 + IL_0075: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_007a: dup + IL_007b: stloc.s V_4 + IL_007d: stfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_0082: ldloc.s V_4 + IL_0084: call instance int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::UseShort(int16) + IL_0089: pop + IL_008a: ldarg.0 + IL_008b: ldfld int16 ICSharpCode.Decompiler.Tests.TestCases.Pretty.InlineAssignmentTest::field4 + IL_0090: call void [mscorlib]System.Console::WriteLine(int32) + IL_0095: ret } // end of method InlineAssignmentTest::SimpleInlineWithFields2 - .method public hidebysig instance void + .method public hidebysig instance int16 UseShort(int16 s) cil managed { - // Code size 7 (0x7) + // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.1 IL_0001: call void [mscorlib]System.Console::WriteLine(int32) - IL_0006: ret + IL_0006: ldarg.1 + IL_0007: ret } // end of method InlineAssignmentTest::UseShort .method public hidebysig instance void diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index f631b5312..00542ee18 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -268,9 +268,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + /// + /// Gets whether 'stobj type(..., value)' would evaluate to a different value than 'value' + /// due to implicit truncation. + /// bool IsImplicitTruncation(ILInstruction value, IType type) { - return type.IsSmallIntegerType(); + if (!type.IsSmallIntegerType()) { + // Implicit truncation in ILAst only happens for small integer types; + // other types of implicit truncation in IL cause the ILReader to insert + // conv instructions. + return false; + } + // With small integer types, test whether the value being stored + // is being truncated: + if (value.MatchLdcI4(out int val)) { + switch (type.GetEnumUnderlyingType().GetDefinition()?.KnownTypeCode) { + case KnownTypeCode.Boolean: + return !(val == 0 || val == 1); + case KnownTypeCode.Byte: + return !(val >= byte.MinValue && val <= byte.MaxValue); + case KnownTypeCode.SByte: + return !(val >= sbyte.MinValue && val <= sbyte.MaxValue); + case KnownTypeCode.Int16: + return !(val >= short.MinValue && val <= short.MaxValue); + case KnownTypeCode.UInt16: + case KnownTypeCode.Char: + return !(val >= ushort.MinValue && val <= ushort.MaxValue); + } + } else if (value is LdObj ldobj) { + return IsImplicitTruncation(ldobj.Type, type); + } else if (value is StObj stobj) { + return IsImplicitTruncation(stobj.Type, type); + } else if (value is LdLoc ldloc) { + return IsImplicitTruncation(ldloc.Variable.Type, type); + } else if (value is StLoc stloc) { + return IsImplicitTruncation(stloc.Variable.Type, type); + } else if (value is CallInstruction call) { + return IsImplicitTruncation(call.Method.ReturnType, type); + } else if (value is Conv conv) { + return conv.TargetType != type.ToPrimitiveType(); + } else if (value is Comp) { + return false; // comp returns 0 or 1, which always fits + } + return true; + } + + bool IsImplicitTruncation(IType fromType, IType toType) + { + return !(fromType.GetSize() <= toType.GetSize() && fromType.GetSign() == toType.GetSign()); } ///