From da5693e605492b23287a15bdd6490ee804a76c27 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 15 Feb 2019 18:14:40 +0100 Subject: [PATCH] Fix #1339: fixed statement not decompiled correctly when pinned variable is reset in finally block --- .../TestCases/Pretty/UnsafeCode.cs | 32 +++- .../TestCases/Pretty/UnsafeCode.il | 137 ++++++++++++++++++ .../TestCases/Pretty/UnsafeCode.opt.il | 123 ++++++++++++++++ .../TestCases/Pretty/UnsafeCode.opt.roslyn.il | 122 ++++++++++++++++ .../TestCases/Pretty/UnsafeCode.roslyn.il | 135 +++++++++++++++++ .../IL/ControlFlow/DetectPinnedRegions.cs | 52 ++++++- .../IL/Instructions/BlockContainer.cs | 14 ++ .../IL/Transforms/ILInlining.cs | 1 + .../IL/Transforms/ReduceNestingTransform.cs | 32 ++++ 9 files changed, 643 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs index cdb640bcb..dc7c727b0 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs @@ -28,7 +28,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public int X; public double Y; } - + + public struct ResultStruct + { + public unsafe byte* ptr1; + public unsafe byte* ptr2; + + public unsafe ResultStruct(byte* ptr1, byte* ptr2) + { + this.ptr1 = ptr1; + this.ptr2 = ptr2; + } + } + public struct StructWithFixedSizeMembers { public unsafe fixed int Integers[100]; @@ -387,5 +399,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty bytePtr -= 4; shortPtr = (short*)((byte*)shortPtr - 3); } + + private static T Get() + { + return default(T); + } + + private unsafe static ResultStruct NestedFixedBlocks(byte[] array) + { + try { + fixed (byte* ptr = array) { + fixed (byte* ptr2 = Get()) { + return new ResultStruct(ptr, ptr2); + } + } + } finally { + Console.WriteLine("Finally"); + } + } } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il index 60913f34e..0b6814b20 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il @@ -39,6 +39,29 @@ .field public float64 Y } // end of class SimpleStruct + .class sequential ansi sealed nested public beforefieldinit ResultStruct + extends [mscorlib]System.ValueType + { + .field public uint8* ptr1 + .field public uint8* ptr2 + .method public hidebysig specialname rtspecialname + instance void .ctor(uint8* ptr1, + uint8* ptr2) cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr1 + IL_0008: ldarg.0 + IL_0009: ldarg.2 + IL_000a: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr2 + IL_000f: ret + } // end of method ResultStruct::.ctor + + } // end of class ResultStruct + .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers extends [mscorlib]System.ValueType { @@ -1456,6 +1479,120 @@ IL_001d: ret } // end of method UnsafeCode::Issue1021 + .method private hidebysig static !!T Get() cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (!!T V_0, + !!T V_1) + IL_0000: nop + IL_0001: ldloca.s V_1 + IL_0003: initobj !!T + IL_0009: ldloc.1 + IL_000a: stloc.0 + IL_000b: br.s IL_000d + + IL_000d: ldloc.0 + IL_000e: ret + } // end of method UnsafeCode::Get + + .method private hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct + NestedFixedBlocks(uint8[] 'array') cil managed + { + // Code size 91 (0x5b) + .maxstack 2 + .locals init (uint8& pinned V_0, + uint8& pinned V_1, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct V_2, + uint8[] V_3) + IL_0000: nop + .try + { + IL_0001: nop + .try + { + IL_0002: ldarg.0 + IL_0003: dup + IL_0004: stloc.3 + IL_0005: brfalse.s IL_000c + + IL_0007: ldloc.3 + IL_0008: ldlen + IL_0009: conv.i4 + IL_000a: brtrue.s IL_0011 + + IL_000c: ldc.i4.0 + IL_000d: conv.u + IL_000e: stloc.0 + IL_000f: br.s IL_0019 + + IL_0011: ldloc.3 + IL_0012: ldc.i4.0 + IL_0013: ldelema [mscorlib]System.Byte + IL_0018: stloc.0 + IL_0019: nop + .try + { + IL_001a: call !!0 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::Get() + IL_001f: dup + IL_0020: stloc.3 + IL_0021: brfalse.s IL_0028 + + IL_0023: ldloc.3 + IL_0024: ldlen + IL_0025: conv.i4 + IL_0026: brtrue.s IL_002d + + IL_0028: ldc.i4.0 + IL_0029: conv.u + IL_002a: stloc.1 + IL_002b: br.s IL_0035 + + IL_002d: ldloc.3 + IL_002e: ldc.i4.0 + IL_002f: ldelema [mscorlib]System.Byte + IL_0034: stloc.1 + IL_0035: nop + IL_0036: ldloc.0 + IL_0037: conv.i + IL_0038: ldloc.1 + IL_0039: conv.i + IL_003a: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::.ctor(uint8*, + uint8*) + IL_003f: stloc.2 + IL_0040: leave.s IL_0058 + + } // end .try + finally + { + IL_0042: ldc.i4.0 + IL_0043: conv.u + IL_0044: stloc.1 + IL_0045: endfinally + } // end handler + } // end .try + finally + { + IL_0046: ldc.i4.0 + IL_0047: conv.u + IL_0048: stloc.0 + IL_0049: endfinally + } // end handler + } // end .try + finally + { + IL_004a: nop + IL_004b: ldstr "Finally" + IL_0050: call void [mscorlib]System.Console::WriteLine(string) + IL_0055: nop + IL_0056: nop + IL_0057: endfinally + } // end handler + IL_0058: nop + IL_0059: ldloc.2 + IL_005a: ret + } // end of method UnsafeCode::NestedFixedBlocks + .property instance int32* NullPointer() { .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il index f3ee817c2..a9daa38da 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il @@ -39,6 +39,28 @@ .field public float64 Y } // end of class SimpleStruct + .class sequential ansi sealed nested public beforefieldinit ResultStruct + extends [mscorlib]System.ValueType + { + .field public uint8* ptr1 + .field public uint8* ptr2 + .method public hidebysig specialname rtspecialname + instance void .ctor(uint8* ptr1, + uint8* ptr2) cil managed + { + // Code size 15 (0xf) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr1 + IL_0007: ldarg.0 + IL_0008: ldarg.2 + IL_0009: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr2 + IL_000e: ret + } // end of method ResultStruct::.ctor + + } // end of class ResultStruct + .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers extends [mscorlib]System.ValueType { @@ -1166,6 +1188,107 @@ IL_001c: ret } // end of method UnsafeCode::Issue1021 + .method private hidebysig static !!T Get() cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (!!T V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj !!T + IL_0008: ldloc.0 + IL_0009: ret + } // end of method UnsafeCode::Get + + .method private hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct + NestedFixedBlocks(uint8[] 'array') cil managed + { + // Code size 86 (0x56) + .maxstack 2 + .locals init (uint8& pinned V_0, + uint8& pinned V_1, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct V_2, + uint8[] V_3, + uint8[] V_4) + .try + { + .try + { + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: stloc.3 + IL_0003: brfalse.s IL_000a + + IL_0005: ldloc.3 + IL_0006: ldlen + IL_0007: conv.i4 + IL_0008: brtrue.s IL_000f + + IL_000a: ldc.i4.0 + IL_000b: conv.u + IL_000c: stloc.0 + IL_000d: br.s IL_0017 + + IL_000f: ldloc.3 + IL_0010: ldc.i4.0 + IL_0011: ldelema [mscorlib]System.Byte + IL_0016: stloc.0 + .try + { + IL_0017: call !!0 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::Get() + IL_001c: dup + IL_001d: stloc.s V_4 + IL_001f: brfalse.s IL_0027 + + IL_0021: ldloc.s V_4 + IL_0023: ldlen + IL_0024: conv.i4 + IL_0025: brtrue.s IL_002c + + IL_0027: ldc.i4.0 + IL_0028: conv.u + IL_0029: stloc.1 + IL_002a: br.s IL_0035 + + IL_002c: ldloc.s V_4 + IL_002e: ldc.i4.0 + IL_002f: ldelema [mscorlib]System.Byte + IL_0034: stloc.1 + IL_0035: ldloc.0 + IL_0036: conv.i + IL_0037: ldloc.1 + IL_0038: conv.i + IL_0039: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::.ctor(uint8*, + uint8*) + IL_003e: stloc.2 + IL_003f: leave.s IL_0054 + + } // end .try + finally + { + IL_0041: ldc.i4.0 + IL_0042: conv.u + IL_0043: stloc.1 + IL_0044: endfinally + } // end handler + } // end .try + finally + { + IL_0045: ldc.i4.0 + IL_0046: conv.u + IL_0047: stloc.0 + IL_0048: endfinally + } // end handler + } // end .try + finally + { + IL_0049: ldstr "Finally" + IL_004e: call void [mscorlib]System.Console::WriteLine(string) + IL_0053: endfinally + } // end handler + IL_0054: ldloc.2 + IL_0055: ret + } // end of method UnsafeCode::NestedFixedBlocks + .property instance int32* NullPointer() { .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il index ec74cf1eb..85b2eb9c8 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il @@ -43,6 +43,28 @@ .field public float64 Y } // end of class SimpleStruct + .class sequential ansi sealed nested public beforefieldinit ResultStruct + extends [mscorlib]System.ValueType + { + .field public uint8* ptr1 + .field public uint8* ptr2 + .method public hidebysig specialname rtspecialname + instance void .ctor(uint8* ptr1, + uint8* ptr2) cil managed + { + // Code size 15 (0xf) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr1 + IL_0007: ldarg.0 + IL_0008: ldarg.2 + IL_0009: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr2 + IL_000e: ret + } // end of method ResultStruct::.ctor + + } // end of class ResultStruct + .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers extends [mscorlib]System.ValueType { @@ -1176,6 +1198,106 @@ IL_001b: ret } // end of method UnsafeCode::Issue1021 + .method private hidebysig static !!T Get() cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (!!T V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj !!T + IL_0008: ldloc.0 + IL_0009: ret + } // end of method UnsafeCode::Get + + .method private hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct + NestedFixedBlocks(uint8[] 'array') cil managed + { + // Code size 84 (0x54) + .maxstack 2 + .locals init (uint8* V_0, + uint8[] pinned V_1, + uint8* V_2, + uint8[] pinned V_3, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct V_4) + .try + { + .try + { + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: stloc.1 + IL_0003: brfalse.s IL_000a + + IL_0005: ldloc.1 + IL_0006: ldlen + IL_0007: conv.i4 + IL_0008: brtrue.s IL_000f + + IL_000a: ldc.i4.0 + IL_000b: conv.u + IL_000c: stloc.0 + IL_000d: br.s IL_0018 + + IL_000f: ldloc.1 + IL_0010: ldc.i4.0 + IL_0011: ldelema [mscorlib]System.Byte + IL_0016: conv.u + IL_0017: stloc.0 + IL_0018: nop + .try + { + IL_0019: call !!0 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::Get() + IL_001e: dup + IL_001f: stloc.3 + IL_0020: brfalse.s IL_0027 + + IL_0022: ldloc.3 + IL_0023: ldlen + IL_0024: conv.i4 + IL_0025: brtrue.s IL_002c + + IL_0027: ldc.i4.0 + IL_0028: conv.u + IL_0029: stloc.2 + IL_002a: br.s IL_0035 + + IL_002c: ldloc.3 + IL_002d: ldc.i4.0 + IL_002e: ldelema [mscorlib]System.Byte + IL_0033: conv.u + IL_0034: stloc.2 + IL_0035: ldloc.0 + IL_0036: ldloc.2 + IL_0037: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::.ctor(uint8*, + uint8*) + IL_003c: stloc.s V_4 + IL_003e: leave.s IL_0051 + + } // end .try + finally + { + IL_0040: ldnull + IL_0041: stloc.3 + IL_0042: endfinally + } // end handler + } // end .try + finally + { + IL_0043: ldnull + IL_0044: stloc.1 + IL_0045: endfinally + } // end handler + } // end .try + finally + { + IL_0046: ldstr "Finally" + IL_004b: call void [mscorlib]System.Console::WriteLine(string) + IL_0050: endfinally + } // end handler + IL_0051: ldloc.s V_4 + IL_0053: ret + } // end of method UnsafeCode::NestedFixedBlocks + .property instance int32* NullPointer() { .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il index 61fed2bf0..aebb7d183 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il @@ -43,6 +43,29 @@ .field public float64 Y } // end of class SimpleStruct + .class sequential ansi sealed nested public beforefieldinit ResultStruct + extends [mscorlib]System.ValueType + { + .field public uint8* ptr1 + .field public uint8* ptr2 + .method public hidebysig specialname rtspecialname + instance void .ctor(uint8* ptr1, + uint8* ptr2) cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr1 + IL_0008: ldarg.0 + IL_0009: ldarg.2 + IL_000a: stfld uint8* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::ptr2 + IL_000f: ret + } // end of method ResultStruct::.ctor + + } // end of class ResultStruct + .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers extends [mscorlib]System.ValueType { @@ -1460,6 +1483,118 @@ IL_001c: ret } // end of method UnsafeCode::Issue1021 + .method private hidebysig static !!T Get() cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (!!T V_0, + !!T V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj !!T + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method UnsafeCode::Get + + .method private hidebysig static valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct + NestedFixedBlocks(uint8[] 'array') cil managed + { + // Code size 90 (0x5a) + .maxstack 2 + .locals init (uint8* V_0, + uint8[] pinned V_1, + uint8* V_2, + uint8[] pinned V_3, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct V_4) + IL_0000: nop + .try + { + IL_0001: nop + .try + { + IL_0002: ldarg.0 + IL_0003: dup + IL_0004: stloc.1 + IL_0005: brfalse.s IL_000c + + IL_0007: ldloc.1 + IL_0008: ldlen + IL_0009: conv.i4 + IL_000a: brtrue.s IL_0011 + + IL_000c: ldc.i4.0 + IL_000d: conv.u + IL_000e: stloc.0 + IL_000f: br.s IL_001a + + IL_0011: ldloc.1 + IL_0012: ldc.i4.0 + IL_0013: ldelema [mscorlib]System.Byte + IL_0018: conv.u + IL_0019: stloc.0 + IL_001a: nop + .try + { + IL_001b: call !!0 ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::Get() + IL_0020: dup + IL_0021: stloc.3 + IL_0022: brfalse.s IL_0029 + + IL_0024: ldloc.3 + IL_0025: ldlen + IL_0026: conv.i4 + IL_0027: brtrue.s IL_002e + + IL_0029: ldc.i4.0 + IL_002a: conv.u + IL_002b: stloc.2 + IL_002c: br.s IL_0037 + + IL_002e: ldloc.3 + IL_002f: ldc.i4.0 + IL_0030: ldelema [mscorlib]System.Byte + IL_0035: conv.u + IL_0036: stloc.2 + IL_0037: nop + IL_0038: ldloc.0 + IL_0039: ldloc.2 + IL_003a: newobj instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode/ResultStruct::.ctor(uint8*, + uint8*) + IL_003f: stloc.s V_4 + IL_0041: leave.s IL_0057 + + } // end .try + finally + { + IL_0043: ldnull + IL_0044: stloc.3 + IL_0045: endfinally + } // end handler + } // end .try + finally + { + IL_0046: ldnull + IL_0047: stloc.1 + IL_0048: endfinally + } // end handler + } // end .try + finally + { + IL_0049: nop + IL_004a: ldstr "Finally" + IL_004f: call void [mscorlib]System.Console::WriteLine(string) + IL_0054: nop + IL_0055: nop + IL_0056: endfinally + } // end handler + IL_0057: ldloc.s V_4 + IL_0059: ret + } // end of method UnsafeCode::NestedFixedBlocks + .property instance int32* NullPointer() { .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index f07e4e59b..0c0b139a4 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -324,7 +324,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } - context.Step("CreatePinnedRegion", block); + context.StepStartGroup($"CreatePinnedRegion {stLoc.Variable.Name}", block); BlockContainer body = new BlockContainer(); for (int i = 0; i < sourceContainer.Blocks.Count; i++) { if (reachedEdgesPerBlock[i] > 0) { @@ -344,10 +344,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // we'll delete the dummy block later } } - - stLoc.ReplaceWith(new PinnedRegion(stLoc.Variable, stLoc.Value, body) { ILRange = stLoc.ILRange }); + + var pinnedRegion = new PinnedRegion(stLoc.Variable, stLoc.Value, body) { ILRange = stLoc.ILRange }; + stLoc.ReplaceWith(pinnedRegion); block.Instructions.RemoveAt(block.Instructions.Count - 1); // remove branch into body - ProcessPinnedRegion((PinnedRegion)block.Instructions.Last()); + CleanUpTryFinallyAroundPinnedRegion(pinnedRegion); + ProcessPinnedRegion(pinnedRegion); + context.StepEndGroup(keepIfEmpty: true); return true; } @@ -614,5 +617,46 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow body.EntryPoint.Instructions.RemoveAt(0); } #endregion + + #region CleanUpTryFinallyAroundPinnedRegion + private void CleanUpTryFinallyAroundPinnedRegion(PinnedRegion pinnedRegion) + { + if (!(pinnedRegion.Parent is Block tryBlock)) + return; + if (!(tryBlock.Parent is BlockContainer tryContainer)) + return; + if (!(tryContainer.Parent is TryFinally tryFinally)) + return; + // .try BlockContainer { + // Block IL_001b (incoming: 1) { + // stloc S_11(call GetArray()) + // PinnedRegion V_3(ldloc S_11, BlockContainer { + // Block IL_0022 (incoming: 1) { + // stloc V_2(conv ref->i (array.to.pointer(ldloc V_3))) + // ... + // } + // ... + // }) + // } + // ... + // } finally BlockContainer { + // Block IL_0043 (incoming: 1) { + // stloc V_3(ldnull) + // leave IL_0043 (nop) + // } + // } + if (!(tryFinally.FinallyBlock is BlockContainer finallyContainer)) + return; + if (finallyContainer.Blocks.Count != 1) + return; + var finallyBlock = finallyContainer.Blocks[0]; + if (finallyBlock.Instructions[0].MatchStLoc(pinnedRegion.Variable, out var value) + && value.MatchLdNull()) { + context.Step("Remove store in finally block", finallyBlock); + finallyBlock.Instructions.RemoveAt(0); + } + return; + } + #endregion } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 2f775bbb2..4bb565db2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -317,6 +317,20 @@ namespace ICSharpCode.Decompiler.IL return false; return true; } + + /// + /// If the container consists of a single block with a single instruction, + /// returns that instruction. + /// Otherwise returns the block, or the container itself if it has multiple blocks. + /// + public ILInstruction SingleInstruction() + { + if (Blocks.Count != 1) + return this; + if (Blocks[0].Instructions.Count != 1) + return Blocks[0]; + return Blocks[0].Instructions[0]; + } } public enum ContainerKind diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 099c41d38..d042d0b3f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -367,6 +367,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) return true; // inline into dynamic compound assignments break; + case OpCode.ArrayToPointer: case OpCode.LocAllocSpan: return true; // inline size-expressions into localloc.span } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs index e791a5c41..2bbefce3a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs @@ -46,6 +46,10 @@ namespace ICSharpCode.Decompiler.IL { this.context = context; Visit((BlockContainer)function.Body, null); + + foreach (var node in function.Descendants.OfType()) { + EliminateRedundantTryFinally(node, context); + } } private void Visit(BlockContainer container, Block continueTarget) @@ -414,5 +418,33 @@ namespace ICSharpCode.Decompiler.IL ifInst.FalseInst = new Nop(); } + + private void EliminateRedundantTryFinally(TryFinally tryFinally, ILTransformContext context) + { + /* The C# compiler sometimes generates try-finally structures for fixed statements. + After our transforms runs, these are redundant and can be removed. + .try BlockContainer { + Block IL_001a (incoming: 1) { + PinnedRegion ... + } + } finally BlockContainer { + Block IL_003e (incoming: 1) { + leave IL_003e (nop) + } + } + ==> PinnedRegion + */ + if (!(tryFinally.FinallyBlock is BlockContainer finallyContainer)) + return; + if (!finallyContainer.SingleInstruction().MatchLeave(finallyContainer)) + return; + // Finally is empty and redundant. But we'll delete the block only if there's a PinnedRegion. + if (!(tryFinally.TryBlock is BlockContainer tryContainer)) + return; + if (tryContainer.SingleInstruction() is PinnedRegion pinnedRegion) { + context.Step("Removing try-finally around PinnedRegion", pinnedRegion); + tryFinally.ReplaceWith(pinnedRegion); + } + } } }