Browse Source

Fix #1339: fixed statement not decompiled correctly when pinned variable is reset in finally block

pull/1423/head
Daniel Grunwald 7 years ago
parent
commit
da5693e605
  1. 32
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs
  2. 137
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il
  3. 123
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il
  4. 122
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il
  5. 135
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il
  6. 52
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  7. 14
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  8. 1
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  9. 32
      ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

32
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.cs

@ -28,7 +28,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public int X; public int X;
public double Y; 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 struct StructWithFixedSizeMembers
{ {
public unsafe fixed int Integers[100]; public unsafe fixed int Integers[100];
@ -387,5 +399,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
bytePtr -= 4; bytePtr -= 4;
shortPtr = (short*)((byte*)shortPtr - 3); shortPtr = (short*)((byte*)shortPtr - 3);
} }
private static T Get<T>()
{
return default(T);
}
private unsafe static ResultStruct NestedFixedBlocks(byte[] array)
{
try {
fixed (byte* ptr = array) {
fixed (byte* ptr2 = Get<byte[]>()) {
return new ResultStruct(ptr, ptr2);
}
}
} finally {
Console.WriteLine("Finally");
}
}
} }
} }

137
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.il

@ -39,6 +39,29 @@
.field public float64 Y .field public float64 Y
} // end of class SimpleStruct } // 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 .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers
extends [mscorlib]System.ValueType extends [mscorlib]System.ValueType
{ {
@ -1456,6 +1479,120 @@
IL_001d: ret IL_001d: ret
} // end of method UnsafeCode::Issue1021 } // end of method UnsafeCode::Issue1021
.method private hidebysig static !!T Get<T>() 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<uint8[]>()
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() .property instance int32* NullPointer()
{ {
.get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer()

123
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.il

@ -39,6 +39,28 @@
.field public float64 Y .field public float64 Y
} // end of class SimpleStruct } // 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 .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers
extends [mscorlib]System.ValueType extends [mscorlib]System.ValueType
{ {
@ -1166,6 +1188,107 @@
IL_001c: ret IL_001c: ret
} // end of method UnsafeCode::Issue1021 } // end of method UnsafeCode::Issue1021
.method private hidebysig static !!T Get<T>() 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<uint8[]>()
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() .property instance int32* NullPointer()
{ {
.get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer()

122
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.opt.roslyn.il

@ -43,6 +43,28 @@
.field public float64 Y .field public float64 Y
} // end of class SimpleStruct } // 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 .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers
extends [mscorlib]System.ValueType extends [mscorlib]System.ValueType
{ {
@ -1176,6 +1198,106 @@
IL_001b: ret IL_001b: ret
} // end of method UnsafeCode::Issue1021 } // end of method UnsafeCode::Issue1021
.method private hidebysig static !!T Get<T>() 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<uint8[]>()
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() .property instance int32* NullPointer()
{ {
.get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer()

135
ICSharpCode.Decompiler.Tests/TestCases/Pretty/UnsafeCode.roslyn.il

@ -43,6 +43,29 @@
.field public float64 Y .field public float64 Y
} // end of class SimpleStruct } // 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 .class sequential ansi sealed nested public beforefieldinit StructWithFixedSizeMembers
extends [mscorlib]System.ValueType extends [mscorlib]System.ValueType
{ {
@ -1460,6 +1483,118 @@
IL_001c: ret IL_001c: ret
} // end of method UnsafeCode::Issue1021 } // end of method UnsafeCode::Issue1021
.method private hidebysig static !!T Get<T>() 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<uint8[]>()
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() .property instance int32* NullPointer()
{ {
.get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer() .get instance int32* ICSharpCode.Decompiler.Tests.TestCases.Pretty.UnsafeCode::get_NullPointer()

52
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(); BlockContainer body = new BlockContainer();
for (int i = 0; i < sourceContainer.Blocks.Count; i++) { for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] > 0) { if (reachedEdgesPerBlock[i] > 0) {
@ -344,10 +344,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// we'll delete the dummy block later // 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 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; return true;
} }
@ -614,5 +617,46 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
body.EntryPoint.Instructions.RemoveAt(0); body.EntryPoint.Instructions.RemoveAt(0);
} }
#endregion #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
} }
} }

14
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -317,6 +317,20 @@ namespace ICSharpCode.Decompiler.IL
return false; return false;
return true; return true;
} }
/// <summary>
/// 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.
/// </summary>
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 public enum ContainerKind

1
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -367,6 +367,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign)
return true; // inline into dynamic compound assignments return true; // inline into dynamic compound assignments
break; break;
case OpCode.ArrayToPointer:
case OpCode.LocAllocSpan: case OpCode.LocAllocSpan:
return true; // inline size-expressions into localloc.span return true; // inline size-expressions into localloc.span
} }

32
ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs

@ -46,6 +46,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
this.context = context; this.context = context;
Visit((BlockContainer)function.Body, null); Visit((BlockContainer)function.Body, null);
foreach (var node in function.Descendants.OfType<TryFinally>()) {
EliminateRedundantTryFinally(node, context);
}
} }
private void Visit(BlockContainer container, Block continueTarget) private void Visit(BlockContainer container, Block continueTarget)
@ -414,5 +418,33 @@ namespace ICSharpCode.Decompiler.IL
ifInst.FalseInst = new Nop(); 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);
}
}
} }
} }

Loading…
Cancel
Save