Browse Source

Fix various bugs with compound assignments.

pull/897/head
Daniel Grunwald 8 years ago
parent
commit
39bb6856b7
  1. 4
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 46
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs
  3. 66
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  4. 149
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.il
  5. 129
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.il
  6. 128
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.roslyn.il
  7. 148
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.roslyn.il
  8. 12
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 5
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  10. 2
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  11. 2
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  12. 19
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  13. 93
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  14. 62
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  15. 33
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  16. 155
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

4
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -32,8 +32,8 @@ @@ -32,8 +32,8 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="1.0.0.55" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="1.3.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.3.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="NUnit" Version="2.6.3" />
<PackageReference Include="NUnitTestAdapter" Version="2.0.0" />
</ItemGroup>

46
ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs

@ -26,6 +26,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -26,6 +26,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static void Main()
{
PreIncrementProperty();
PreIncrementIndexer();
CallTwice();
UnsignedShiftRightInstanceField();
UnsignedShiftRightStaticProperty();
}
static void Test(int a, int b)
@ -41,16 +45,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -41,16 +45,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
return ++x;
}
static int instanceCount;
int instanceNumber = ++instanceCount;
int instanceField;
public int InstanceProperty
{
get {
Console.WriteLine("In get_InstanceProperty");
Console.WriteLine("In {0}.get_InstanceProperty", instanceNumber);
return instanceField;
}
set {
Console.WriteLine("In set_InstanceProperty, value=" + value);
Console.WriteLine("In {0}.set_InstanceProperty, value=" + value, instanceNumber);
instanceField = value;
}
}
@ -72,7 +79,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -72,7 +79,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static Dictionary<string, int> GetDict()
{
Console.WriteLine("In GetDict()");
return new Dictionary<string, int>();
return new Dictionary<string, int>() { { GetString(), 5 } };
}
static CompoundAssignment GetObject()
{
Console.WriteLine("In GetObject() (instance #)");
return new CompoundAssignment();
}
static string GetString()
@ -93,5 +106,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -93,5 +106,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine("PreIncrementIndexer:");
Test(X(), ++GetDict()[GetString()]);
}
static void CallTwice()
{
Console.WriteLine("CallTwice: instanceField:");
GetObject().instanceField = GetObject().instanceField + 1;
Test(X(), GetObject().instanceField = GetObject().instanceField + 1);
Console.WriteLine("CallTwice: InstanceProperty:");
GetObject().InstanceProperty = GetObject().InstanceProperty + 1;
Test(X(), GetObject().InstanceProperty = GetObject().InstanceProperty + 1);
Console.WriteLine("CallTwice: dict indexer:");
GetDict()[GetString()] = GetDict()[GetString()] + 1;
Test(X(), GetDict()[GetString()] = GetDict()[GetString()] + 1);
}
static void UnsignedShiftRightInstanceField()
{
#if !LEGACY_CSC
ref int f = ref new CompoundAssignment().instanceField;
Test(X(), f = (int)((uint)f >> 2));
#endif
}
static void UnsignedShiftRightStaticProperty()
{
StaticProperty = -15;
Test(X(), StaticProperty = (int)((uint)StaticProperty >> 2));
}
}
}

66
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -219,32 +219,92 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -219,32 +219,92 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return --array[pos];
}
public int PostIncrementArrayElement(int[] array, int pos)
{
return array[pos]++;
}
public void IncrementArrayElement(int[] array, int pos)
{
array[pos]++;
}
public int PreIncrementInstanceField()
{
return ++this.M().Field;
}
//public int PostIncrementInstanceField()
//{
// return this.M().Field++;
//}
public void IncrementInstanceField()
{
this.M().Field++;
}
public int PreIncrementInstanceField2(MutableClass m)
{
return ++m.Field;
}
//public int PostIncrementInstanceField2(MutableClass m)
//{
// return m.Field++;
//}
public void IncrementInstanceField2(MutableClass m)
{
m.Field++;
}
public int PreIncrementInstanceProperty()
{
return ++this.M().Property;
}
//public int PostIncrementInstanceProperty()
//{
// return this.M().Property++;
//}
public void IncrementInstanceProperty()
{
this.M().Property++;
}
public int PreIncrementStaticField()
{
return ++CompoundAssignmentTest.StaticField;
}
public int PostIncrementStaticField()
{
return CompoundAssignmentTest.StaticField++;
}
public void IncrementStaticField()
{
CompoundAssignmentTest.StaticField++;
}
public int PreIncrementStaticProperty()
{
return ++CompoundAssignmentTest.StaticProperty;
}
//public int PostIncrementStaticProperty()
//{
// return CompoundAssignmentTest.StaticProperty++;
//}
public void IncrementStaticProperty()
{
CompoundAssignmentTest.StaticProperty++;
}
private static Item GetItem(object obj)
{
return null;

149
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly wswrh1ww
.assembly kiryblux
{
.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 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module wswrh1ww.dll
// MVID: {0FA9B26D-B874-4697-B526-76EF6D2DAD90}
.module kiryblux.dll
// MVID: {34EC794D-1FFD-488F-9BDC-2D5D9C15854D}
.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: 0x029E0000
// Image base: 0x02A30000
// =============== CLASS MEMBERS DECLARATION ===================
@ -762,6 +762,51 @@ @@ -762,6 +762,51 @@
IL_001c: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement
.method public hidebysig instance int32
PostIncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 29 (0x1d)
.maxstack 3
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldelema [mscorlib]System.Int32
IL_0008: dup
IL_0009: ldobj [mscorlib]System.Int32
IL_000e: dup
IL_000f: stloc.1
IL_0010: ldc.i4.1
IL_0011: add
IL_0012: stobj [mscorlib]System.Int32
IL_0017: ldloc.1
IL_0018: stloc.0
IL_0019: br.s IL_001b
IL_001b: ldloc.0
IL_001c: ret
} // end of method CompoundAssignmentTest::PostIncrementArrayElement
.method public hidebysig instance void
IncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 22 (0x16)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldelema [mscorlib]System.Int32
IL_0008: dup
IL_0009: ldobj [mscorlib]System.Int32
IL_000e: ldc.i4.1
IL_000f: add
IL_0010: stobj [mscorlib]System.Int32
IL_0015: ret
} // end of method CompoundAssignmentTest::IncrementArrayElement
.method public hidebysig instance int32
PreIncrementInstanceField() cil managed
{
@ -787,6 +832,22 @@ @@ -787,6 +832,22 @@
IL_001b: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField
.method public hidebysig instance void
IncrementInstanceField() cil managed
{
// Code size 21 (0x15)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0007: dup
IL_0008: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0014: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField
.method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
@ -811,6 +872,21 @@ @@ -811,6 +872,21 @@
IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2
.method public hidebysig instance void
IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: dup
IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0008: ldc.i4.1
IL_0009: add
IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000f: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField2
.method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed
{
@ -837,6 +913,23 @@ @@ -837,6 +913,23 @@
IL_001c: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty
.method public hidebysig instance void
IncrementInstanceProperty() cil managed
{
// Code size 22 (0x16)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0007: dup
IL_0008: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property()
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32)
IL_0014: nop
IL_0015: ret
} // end of method CompoundAssignmentTest::IncrementInstanceProperty
.method public hidebysig instance int32
PreIncrementStaticField() cil managed
{
@ -856,6 +949,38 @@ @@ -856,6 +949,38 @@
IL_0012: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField
.method public hidebysig instance int32
PostIncrementStaticField() cil managed
{
// Code size 19 (0x13)
.maxstack 3
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0006: dup
IL_0007: ldc.i4.1
IL_0008: add
IL_0009: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000e: stloc.0
IL_000f: br.s IL_0011
IL_0011: ldloc.0
IL_0012: ret
} // end of method CompoundAssignmentTest::PostIncrementStaticField
.method public hidebysig instance void
IncrementStaticField() cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000d: ret
} // end of method CompoundAssignmentTest::IncrementStaticField
.method public hidebysig instance int32
PreIncrementStaticProperty() cil managed
{
@ -876,6 +1001,20 @@ @@ -876,6 +1001,20 @@
IL_0013: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty
.method public hidebysig instance void
IncrementStaticProperty() cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: nop
IL_0001: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty()
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32)
IL_000d: nop
IL_000e: ret
} // end of method CompoundAssignmentTest::IncrementStaticProperty
.method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed
{

129
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly ag42vicc
.assembly d4bhqxbe
{
.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 @@ @@ -20,15 +20,15 @@
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module ag42vicc.dll
// MVID: {EB752380-6A62-4B20-9497-B68A784C5BD7}
.module d4bhqxbe.dll
// MVID: {B884BD61-6FE3-4893-B161-02EE675C0DF0}
.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: 0x00DE0000
// Image base: 0x003A0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -647,6 +647,44 @@ @@ -647,6 +647,44 @@
IL_0017: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement
.method public hidebysig instance int32
PostIncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 24 (0x18)
.maxstack 3
.locals init (int32 V_0)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: ldelema [mscorlib]System.Int32
IL_0007: dup
IL_0008: ldobj [mscorlib]System.Int32
IL_000d: dup
IL_000e: stloc.0
IL_000f: ldc.i4.1
IL_0010: add
IL_0011: stobj [mscorlib]System.Int32
IL_0016: ldloc.0
IL_0017: ret
} // end of method CompoundAssignmentTest::PostIncrementArrayElement
.method public hidebysig instance void
IncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 21 (0x15)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: ldelema [mscorlib]System.Int32
IL_0007: dup
IL_0008: ldobj [mscorlib]System.Int32
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: stobj [mscorlib]System.Int32
IL_0014: ret
} // end of method CompoundAssignmentTest::IncrementArrayElement
.method public hidebysig instance int32
PreIncrementInstanceField() cil managed
{
@ -666,6 +704,21 @@ @@ -666,6 +704,21 @@
IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField
.method public hidebysig instance void
IncrementInstanceField() cil managed
{
// Code size 20 (0x14)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0006: dup
IL_0007: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000c: ldc.i4.1
IL_000d: add
IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0013: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField
.method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
@ -684,6 +737,20 @@ @@ -684,6 +737,20 @@
IL_0011: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2
.method public hidebysig instance void
IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: ldarg.1
IL_0001: dup
IL_0002: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0007: ldc.i4.1
IL_0008: add
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000e: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField2
.method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed
{
@ -703,6 +770,21 @@ @@ -703,6 +770,21 @@
IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty
.method public hidebysig instance void
IncrementInstanceProperty() cil managed
{
// Code size 20 (0x14)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0006: dup
IL_0007: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property()
IL_000c: ldc.i4.1
IL_000d: add
IL_000e: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32)
IL_0013: ret
} // end of method CompoundAssignmentTest::IncrementInstanceProperty
.method public hidebysig instance int32
PreIncrementStaticField() cil managed
{
@ -716,6 +798,31 @@ @@ -716,6 +798,31 @@
IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField
.method public hidebysig instance int32
PostIncrementStaticField() cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0005: dup
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000d: ret
} // end of method CompoundAssignmentTest::PostIncrementStaticField
.method public hidebysig instance void
IncrementStaticField() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000c: ret
} // end of method CompoundAssignmentTest::IncrementStaticField
.method public hidebysig instance int32
PreIncrementStaticProperty() cil managed
{
@ -729,6 +836,18 @@ @@ -729,6 +836,18 @@
IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty
.method public hidebysig instance void
IncrementStaticProperty() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty()
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32)
IL_000c: ret
} // end of method CompoundAssignmentTest::IncrementStaticProperty
.method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed
{

128
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.opt.roslyn.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module CompoundAssignmentTest.dll
// MVID: {104CDF7A-FBAC-406D-AA60-4C777388A178}
// MVID: {6E5749EE-F4C5-4B86-96FC-F4A665551BA6}
.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: 0x008E0000
// Image base: 0x028A0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -650,6 +650,44 @@ @@ -650,6 +650,44 @@
IL_000f: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement
.method public hidebysig instance int32
PostIncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 16 (0x10)
.maxstack 3
.locals init (int32 V_0)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: ldelema [mscorlib]System.Int32
IL_0007: dup
IL_0008: ldind.i4
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: ldc.i4.1
IL_000c: add
IL_000d: stind.i4
IL_000e: ldloc.0
IL_000f: ret
} // end of method CompoundAssignmentTest::PostIncrementArrayElement
.method public hidebysig instance void
IncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: ldelema [mscorlib]System.Int32
IL_0007: dup
IL_0008: ldind.i4
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stind.i4
IL_000c: ret
} // end of method CompoundAssignmentTest::IncrementArrayElement
.method public hidebysig instance int32
PreIncrementInstanceField() cil managed
{
@ -669,6 +707,21 @@ @@ -669,6 +707,21 @@
IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField
.method public hidebysig instance void
IncrementInstanceField() cil managed
{
// Code size 20 (0x14)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0006: dup
IL_0007: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000c: ldc.i4.1
IL_000d: add
IL_000e: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0013: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField
.method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
@ -687,6 +740,20 @@ @@ -687,6 +740,20 @@
IL_0011: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2
.method public hidebysig instance void
IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: ldarg.1
IL_0001: dup
IL_0002: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0007: ldc.i4.1
IL_0008: add
IL_0009: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000e: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField2
.method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed
{
@ -706,6 +773,24 @@ @@ -706,6 +773,24 @@
IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty
.method public hidebysig instance void
IncrementInstanceProperty() cil managed
{
// Code size 22 (0x16)
.maxstack 3
.locals init (int32 V_0)
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0006: dup
IL_0007: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property()
IL_000c: stloc.0
IL_000d: ldloc.0
IL_000e: ldc.i4.1
IL_000f: add
IL_0010: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32)
IL_0015: ret
} // end of method CompoundAssignmentTest::IncrementInstanceProperty
.method public hidebysig instance int32
PreIncrementStaticField() cil managed
{
@ -719,6 +804,31 @@ @@ -719,6 +804,31 @@
IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField
.method public hidebysig instance int32
PostIncrementStaticField() cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0005: dup
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000d: ret
} // end of method CompoundAssignmentTest::PostIncrementStaticField
.method public hidebysig instance void
IncrementStaticField() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000c: ret
} // end of method CompoundAssignmentTest::IncrementStaticField
.method public hidebysig instance int32
PreIncrementStaticProperty() cil managed
{
@ -732,6 +842,18 @@ @@ -732,6 +842,18 @@
IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty
.method public hidebysig instance void
IncrementStaticProperty() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty()
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32)
IL_000c: ret
} // end of method CompoundAssignmentTest::IncrementStaticProperty
.method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed
{

148
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.roslyn.il

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.17929
// Microsoft (R) .NET Framework IL Disassembler. Version 4.0.30319.18020
// Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@ @@ -25,14 +25,14 @@
.ver 0:0:0:0
}
.module CompoundAssignmentTest.dll
// MVID: {E4D6BDFE-1178-4790-A328-6755C028F1BE}
// MVID: {391C1968-5381-4AC7-9C19-7B5F6ED36AF8}
.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: 0x00D10000
// Image base: 0x003D0000
// =============== CLASS MEMBERS DECLARATION ===================
@ -759,6 +759,51 @@ @@ -759,6 +759,51 @@
IL_0014: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement
.method public hidebysig instance int32
PostIncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 21 (0x15)
.maxstack 3
.locals init (int32 V_0,
int32 V_1)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldelema [mscorlib]System.Int32
IL_0008: dup
IL_0009: ldind.i4
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: ldc.i4.1
IL_000d: add
IL_000e: stind.i4
IL_000f: ldloc.0
IL_0010: stloc.1
IL_0011: br.s IL_0013
IL_0013: ldloc.1
IL_0014: ret
} // end of method CompoundAssignmentTest::PostIncrementArrayElement
.method public hidebysig instance void
IncrementArrayElement(int32[] 'array',
int32 pos) cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: ldelema [mscorlib]System.Int32
IL_0008: dup
IL_0009: ldind.i4
IL_000a: ldc.i4.1
IL_000b: add
IL_000c: stind.i4
IL_000d: ret
} // end of method CompoundAssignmentTest::IncrementArrayElement
.method public hidebysig instance int32
PreIncrementInstanceField() cil managed
{
@ -784,6 +829,22 @@ @@ -784,6 +829,22 @@
IL_001b: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField
.method public hidebysig instance void
IncrementInstanceField() cil managed
{
// Code size 21 (0x15)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0007: dup
IL_0008: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000d: ldc.i4.1
IL_000e: add
IL_000f: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0014: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField
.method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
@ -808,6 +869,21 @@ @@ -808,6 +869,21 @@
IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2
.method public hidebysig instance void
IncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: dup
IL_0003: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_0008: ldc.i4.1
IL_0009: add
IL_000a: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::Field
IL_000f: ret
} // end of method CompoundAssignmentTest::IncrementInstanceField2
.method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed
{
@ -834,6 +910,26 @@ @@ -834,6 +910,26 @@
IL_001c: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty
.method public hidebysig instance void
IncrementInstanceProperty() cil managed
{
// Code size 24 (0x18)
.maxstack 3
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::M()
IL_0007: dup
IL_0008: callvirt instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::get_Property()
IL_000d: stloc.0
IL_000e: ldloc.0
IL_000f: ldc.i4.1
IL_0010: add
IL_0011: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass::set_Property(int32)
IL_0016: nop
IL_0017: ret
} // end of method CompoundAssignmentTest::IncrementInstanceProperty
.method public hidebysig instance int32
PreIncrementStaticField() cil managed
{
@ -853,6 +949,38 @@ @@ -853,6 +949,38 @@
IL_0012: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField
.method public hidebysig instance int32
PostIncrementStaticField() cil managed
{
// Code size 19 (0x13)
.maxstack 3
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0006: dup
IL_0007: ldc.i4.1
IL_0008: add
IL_0009: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000e: stloc.0
IL_000f: br.s IL_0011
IL_0011: ldloc.0
IL_0012: ret
} // end of method CompoundAssignmentTest::PostIncrementStaticField
.method public hidebysig instance void
IncrementStaticField() cil managed
{
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: stsfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::StaticField
IL_000d: ret
} // end of method CompoundAssignmentTest::IncrementStaticField
.method public hidebysig instance int32
PreIncrementStaticProperty() cil managed
{
@ -873,6 +1001,20 @@ @@ -873,6 +1001,20 @@
IL_0013: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty
.method public hidebysig instance void
IncrementStaticProperty() cil managed
{
// Code size 15 (0xf)
.maxstack 8
IL_0000: nop
IL_0001: call int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::get_StaticProperty()
IL_0006: ldc.i4.1
IL_0007: add
IL_0008: call void ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest::set_StaticProperty(int32)
IL_000d: nop
IL_000e: ret
} // end of method CompoundAssignmentTest::IncrementStaticProperty
.method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed
{

12
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -75,12 +75,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -75,12 +75,8 @@ namespace ICSharpCode.Decompiler.CSharp
new AsyncAwaitDecompiler(), // must run after inlining but before loop detection
new DetectCatchWhenConditionBlocks(), // must run after inlining but before loop detection
new DetectExitPoints(canIntroduceExitForReturn: false),
new BlockILTransform {
PostOrderTransforms = {
new ExpressionTransforms() // for RemoveDeadVariableInit
}
},
// RemoveDeadVariableInit must run after ExpressionTransforms so that stobj(ldloca V, ...)
new EarlyExpressionTransforms(),
// RemoveDeadVariableInit must run after EarlyExpressionTransforms so that stobj(ldloca V, ...)
// is already collapsed into stloc(V, ...).
new RemoveDeadVariableInit(),
new SplitVariables(), // split variables once again, because the stobj(ldloca V, ...) may open up new replacements
@ -107,8 +103,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -107,8 +103,7 @@ namespace ICSharpCode.Decompiler.CSharp
// and must run before NullCoalescingTransform
new CachedDelegateInitialization(),
new ILInlining(),
new TransformAssignment(),
new NullableLiftingBlockTransform(),
new TransformAssignment(), // must run before CopyPropagation
new CopyPropagation(),
new StatementTransform(
// per-block transforms that depend on each other, and thus need to
@ -120,6 +115,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -120,6 +115,7 @@ namespace ICSharpCode.Decompiler.CSharp
// Any other transform that opens up new inlining opportunities should call RequestRerun().
new ExpressionTransforms(),
new NullCoalescingTransform(),
new NullableLiftingStatementTransform(),
new TransformArrayInitializers(),
new TransformCollectionAndObjectInitializers()
)

5
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -49,8 +49,8 @@ @@ -49,8 +49,8 @@
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
@ -271,6 +271,7 @@ @@ -271,6 +271,7 @@
<Compile Include="DotNetCore\DotNetCorePathFinder.cs" />
<Compile Include="DotNetCore\DotNetCorePathFinderExtensions.cs" />
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" />
<Compile Include="IL\Transforms\UsingTransform.cs" />
<Compile Include="IL\ControlFlow\AsyncAwaitDecompiler.cs" />
<Compile Include="IL\ControlFlow\ControlFlowGraph.cs" />

2
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -127,7 +127,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -127,7 +127,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
context.StepStartGroup("CleanUpBodyOfMoveNext", function);
// Simplify stobj(ldloca) -> stloc
foreach (var stobj in function.Descendants.OfType<StObj>()) {
ExpressionTransforms.StObjToStLoc(stobj, context);
EarlyExpressionTransforms.StObjToStLoc(stobj, context);
}
// Copy-propagate temporaries holding a copy of 'this'.

2
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -146,7 +146,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -146,7 +146,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
targetBlock.Instructions.AddRange(nestedTrueBlock.Instructions);
// add falseInsts to inner block
nestedTrueBlock.Instructions.ReplaceList(falseInsts);
nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms { context = context });
nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms { context = new StatementTransformContext(context) });
}
break;
}

19
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL @@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL
public CompoundAssignmentInstruction(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType)
: base(OpCode.CompoundAssignmentInstruction)
{
Debug.Assert(IsBinaryCompatibleWithType(binary, type));
this.CheckForOverflow = binary.CheckForOverflow;
this.Sign = binary.Sign;
this.LeftInputType = binary.LeftInputType;
@ -69,10 +70,28 @@ namespace ICSharpCode.Decompiler.IL @@ -69,10 +70,28 @@ namespace ICSharpCode.Decompiler.IL
this.Target = target;
this.type = type;
this.Value = value;
this.ILRange = binary.ILRange;
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(IsValidCompoundAssignmentTarget(Target));
}
/// <summary>
/// Gets whether the specific binary instruction is compatible with a compound operation on the specified type.
/// </summary>
internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type)
{
if (binary.IsLifted) {
if (!NullableType.IsNullable(type))
return false;
type = NullableType.GetUnderlyingType(type);
}
if (binary.Sign != Sign.None) {
if (type.GetSign() != binary.Sign)
return false;
}
return true;
}
internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst)
{
switch (inst.OpCode) {

93
ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
// Copyright (c) 2017 Daniel Grunwald
//
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class EarlyExpressionTransforms : ILVisitor, IILTransform
{
ILTransformContext context;
public void Run(ILFunction function, ILTransformContext context)
{
this.context = context;
Default(function);
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children) {
child.AcceptVisitor(this);
}
}
protected internal override void VisitStObj(StObj inst)
{
base.VisitStObj(inst);
StObjToStLoc(inst, context);
}
// This transform is required because ILInlining only works with stloc/ldloc
internal static bool StObjToStLoc(StObj inst, ILTransformContext context)
{
if (inst.Target.MatchLdLoca(out ILVariable v)
&& TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type)
&& inst.UnalignedPrefix == 0
&& !inst.IsVolatile) {
context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst);
inst.ReplaceWith(new StLoc(v, inst.Value));
return true;
}
return false;
}
protected internal override void VisitCall(Call inst)
{
var expr = HandleCall(inst, context);
if (expr != null) {
// The resulting expression may trigger further rules, so continue visiting the replacement:
expr.AcceptVisitor(this);
} else {
base.VisitCall(inst);
}
}
internal static ILInstruction HandleCall(Call inst, ILTransformContext context)
{
if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) {
Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1);
context.Step("Transform call to struct constructor", inst);
// call(ref, ...)
// => stobj(ref, newobj(...))
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);
inst.ReplaceWith(expr);
return expr;
}
return null;
}
}
}

62
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2014 Daniel Grunwald
// Copyright (c) 2014-2017 Daniel Grunwald
//
// 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
@ -29,16 +29,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -29,16 +29,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <remarks>
/// Should run after inlining so that the expression patterns can be detected.
/// </remarks>
public class ExpressionTransforms : ILVisitor, IBlockTransform, IStatementTransform
public class ExpressionTransforms : ILVisitor, IStatementTransform
{
internal ILTransformContext context;
public void Run(Block block, BlockTransformContext context)
{
this.context = context;
Default(block);
}
internal StatementTransformContext context;
public void Run(Block block, int pos, StatementTransformContext context)
{
this.context = context;
@ -229,23 +223,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -229,23 +223,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitCall(Call inst)
{
if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) {
Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1);
context.Step("Transform call to struct constructor", inst);
// call(ref, ...)
// => stobj(ref, newobj(...))
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);
inst.ReplaceWith(expr);
// Both the StObj and the NewObj may trigger further rules, so continue visiting the replacement:
VisitStObj(expr);
var expr = EarlyExpressionTransforms.HandleCall(inst, context);
if (expr != null) {
// The resulting expression may trigger further rules, so continue visiting the replacement:
expr.AcceptVisitor(this);
} else {
base.VisitCall(inst);
TransformAssignment.HandleCallCompoundAssign(inst, context);
}
}
protected internal override void VisitCallVirt(CallVirt inst)
{
base.VisitCallVirt(inst);
TransformAssignment.HandleCallCompoundAssign(inst, context);
}
protected internal override void VisitNewObj(NewObj inst)
{
LdcDecimal decimalConstant;
@ -286,13 +279,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -286,13 +279,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStObj(StObj inst)
{
base.VisitStObj(inst);
if (StObjToStLoc(inst, context)) {
if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) {
context.RequestRerun();
return;
}
if (inst.Value is BinaryNumericInstruction binary
&& binary.Left.MatchLdObj(out ILInstruction target, out IType t)
&& inst.Target.Match(target).Success)
&& inst.Target.Match(target).Success
&& SemanticHelper.IsPure(target.Flags)
&& CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, t))
{
context.Step("compound assignment", inst);
// stobj(target, binary.op(ldobj(target), ...))
@ -303,20 +299,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -303,20 +299,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
// This transform is required because ILInlining only works with stloc/ldloc
internal static bool StObjToStLoc(StObj inst, ILTransformContext context)
{
if (inst.Target.MatchLdLoca(out ILVariable v)
&& TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type)
&& inst.UnalignedPrefix == 0
&& !inst.IsVolatile) {
context.Step($"stobj(ldloca {v.Name}, ...) => stloc {v.Name}(...)", inst);
inst.ReplaceWith(new StLoc(v, inst.Value));
return true;
}
return false;
}
protected internal override void VisitIfInstruction(IfInstruction inst)
{
inst.TrueInst.AcceptVisitor(this);
@ -360,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -360,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1);
newIf.ILRange = inst.ILRange;
inst.ReplaceWith(new StLoc(v, newIf));
(context as StatementTransformContext)?.RequestRerun(); // trigger potential inlining of the newly created StLoc
context.RequestRerun(); // trigger potential inlining of the newly created StLoc
return newIf;
}
return inst;

33
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
public bool RunBlock(Block block)
public bool RunStatements(Block block, int pos)
{
if (!context.Settings.LiftNullables)
return false;
@ -73,18 +73,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -73,18 +73,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// leave IL_0000 (default.value System.Nullable`1[[System.Int64]])
// }
// leave IL_0000 (newobj .ctor(exprToLift))
IfInstruction ifInst;
if (block.Instructions.Last() is Leave elseLeave) {
ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction;
if (ifInst == null || !ifInst.FalseInst.MatchNop())
return false;
} else {
if (pos != block.Instructions.Count - 2)
return false;
if (!(block.Instructions[pos] is IfInstruction ifInst))
return false;
}
if (!(Block.Unwrap(ifInst.TrueInst) is Leave thenLeave))
return false;
if (!ifInst.FalseInst.MatchNop())
return false;
if (!(block.Instructions[pos + 1] is Leave elseLeave))
return false;
if (elseLeave.TargetContainer != thenLeave.TargetContainer)
return false;
var lifted = Lift(ifInst, thenLeave.Value, elseLeave.Value);
if (lifted != null) {
thenLeave.Value = lifted;
@ -149,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -149,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// => a != b
return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst)
?? LiftCSharpUserEqualityComparison(comp, ComparisonKind.Inequality, trueInst);
} else if (IsGenericNewPattern(condition, trueInst, falseInst)) {
} else if (IsGenericNewPattern(comp.Left, comp.Right, trueInst, falseInst)) {
// (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
// => Activator.CreateInstance<T>()
return trueInst;
@ -245,16 +247,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -245,16 +247,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
}
private bool IsGenericNewPattern(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst)
private bool IsGenericNewPattern(ILInstruction compLeft, ILInstruction compRight, ILInstruction trueInst, ILInstruction falseInst)
{
// (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
return falseInst.MatchDefaultValue(out var type) &&
(trueInst is Call c && c.Method.FullName == "System.Activator.CreateInstance" && c.Method.TypeArguments.Count == 1) &&
type.Kind == TypeKind.TypeParameter &&
condition.MatchCompEquals(out var left, out var right) &&
left.MatchDefaultValue(out var type2) &&
compLeft.MatchDefaultValue(out var type2) &&
type.Equals(type2) &&
right.MatchLdNull();
compRight.MatchLdNull();
}
private bool MatchThreeValuedLogicConditionPattern(ILInstruction condition, out ILVariable nullable1, out ILVariable nullable2)
@ -847,11 +848,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -847,11 +848,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
#endregion
}
class NullableLiftingBlockTransform : IBlockTransform
class NullableLiftingStatementTransform : IStatementTransform
{
public void Run(Block block, BlockTransformContext context)
public void Run(Block block, int pos, StatementTransformContext context)
{
new NullableLiftingTransform(context).RunBlock(block);
new NullableLiftingTransform(context).RunStatements(block, pos);
}
}
}

155
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -16,13 +16,14 @@ @@ -16,13 +16,14 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Description of TransformAssignment.
/// Constructs compound assignments and inline assignments.
/// </summary>
public class TransformAssignment : IBlockTransform
{
@ -42,7 +43,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -42,7 +43,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (TransformInlineAssignmentStObj(block, i))
continue;
if (TransformInlineAssignmentCall(block, i))
if (TransformInlineCompoundAssignmentCall(block, i))
continue;
if (TransformRoslynCompoundAssignmentCall(block, i))
continue;
}
}
@ -82,8 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -82,8 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
} else { // otherwise it must be local
TransformInlineAssignmentLocal(block, i);
return false;
return TransformInlineAssignmentLocal(block, i);
}
context.Step("Inline assignment to instance field", fieldStore);
local = localStore.Variable;
@ -106,76 +108,151 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -106,76 +108,151 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <code>
/// stloc s(binary(callvirt(getter), value))
/// callvirt (setter, ldloc s)
/// ... usage of s ...
/// -->
/// ... compound.op.new(callvirt(getter), value) ...
/// </code>
/// -or-
/// <code>
/// stloc s(stloc v(binary(callvirt(getter), value)))
/// callvirt (setter, ldloc s)
/// ... usage of v ...
/// (followed by single usage of s in next instruction)
/// -->
/// ... compound.op.new(callvirt(getter), value) ...
/// stloc s(compound.op.new(callvirt(getter), value))
/// </code>
bool TransformInlineAssignmentCall(Block block, int i)
bool TransformInlineCompoundAssignmentCall(Block block, int i)
{
var inst = block.Instructions[i] as StLoc;
var mainStLoc = block.Instructions[i] as StLoc;
// in some cases it can be a compiler-generated local
if (inst == null || (inst.Variable.Kind != VariableKind.StackSlot && inst.Variable.Kind != VariableKind.Local))
if (mainStLoc == null || (mainStLoc.Variable.Kind != VariableKind.StackSlot && mainStLoc.Variable.Kind != VariableKind.Local))
return false;
BinaryNumericInstruction binary = mainStLoc.Value as BinaryNumericInstruction;
ILVariable localVariable = mainStLoc.Variable;
if (!localVariable.IsSingleDefinition)
return false;
if (localVariable.LoadCount != 2)
return false;
BinaryNumericInstruction binary;
ILVariable localVariable;
if (inst.Value is StLoc) {
var tmp = (StLoc)inst.Value;
binary = tmp.Value as BinaryNumericInstruction;
localVariable = tmp.Variable;
} else {
binary = inst.Value as BinaryNumericInstruction;
localVariable = inst.Variable;
}
var getterCall = binary?.Left as CallInstruction;
var setterCall = block.Instructions.ElementAtOrDefault(i + 1) as CallInstruction;
if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner))
if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
return false;
var owner = getterCall.Method.AccessorOwner as IProperty;
if (owner == null || !IsSameMember(getterCall.Method, owner.Getter) || !IsSameMember(setterCall.Method, owner.Setter))
if (!setterCall.Arguments.Last().MatchLdLoc(localVariable))
return false;
var next = block.Instructions.ElementAtOrDefault(i + 2);
if (next == null)
return false;
var usages = next.Descendants.Where(d => d.MatchLdLoc(localVariable)).ToArray();
if (usages.Length != 1)
if (next.Descendants.Where(d => d.MatchLdLoc(localVariable)).Count() != 1)
return false;
context.Step($"Compound assignment to '{owner.Name}'", setterCall);
block.Instructions.RemoveAt(i + 1);
block.Instructions.RemoveAt(i);
usages[0].ReplaceWith(new CompoundAssignmentInstruction(
if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
return false;
context.Step($"Inline compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
block.Instructions.RemoveAt(i + 1); // remove setter call
binary.ReplaceWith(new CompoundAssignmentInstruction(
binary, getterCall, binary.Right,
getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue));
return true;
}
/// <summary>
/// Roslyn compound assignment that's not inline within another instruction.
/// </summary>
bool TransformRoslynCompoundAssignmentCall(Block block, int i)
{
// stloc variable(callvirt get_Property(ldloc obj))
// callvirt set_Property(ldloc obj, binary.op(ldloc variable, ldc.i4 1))
// => compound.op.new(callvirt get_Property(ldloc obj), ldc.i4 1)
if (!(block.Instructions[i] is StLoc stloc))
return false;
if (!(stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 1))
return false;
var getterCall = stloc.Value as CallInstruction;
var setterCall = block.Instructions[i + 1] as CallInstruction;
if (!(MatchingGetterAndSetterCalls(getterCall, setterCall)))
return false;
var binary = setterCall.Arguments.Last() as BinaryNumericInstruction;
if (binary == null || !binary.Left.MatchLdLoc(stloc.Variable))
return false;
if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
return false;
context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
block.Instructions.RemoveAt(i + 1); // remove setter call
stloc.ReplaceWith(new CompoundAssignmentInstruction(
binary, getterCall, binary.Right,
getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue));
return true;
}
static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall)
{
if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner))
return false;
var owner = getterCall.Method.AccessorOwner as IProperty;
if (owner == null || !IsSameMember(getterCall.Method, owner.Getter) || !IsSameMember(setterCall.Method, owner.Setter))
return false;
if (setterCall.Arguments.Count != getterCall.Arguments.Count + 1)
return false;
// Ensure that same arguments are passed to getterCall and setterCall:
for (int j = 0; j < getterCall.Arguments.Count; j++) {
if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags))
return false;
if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success)
return false;
}
return true;
}
/// <summary>
/// Transform compound assignments where the return value is not being used,
/// or where there's an inlined assignment within the setter call.
/// </summary>
/// <remarks>
/// Called by ExpressionTransforms.
/// </remarks>
internal static bool HandleCallCompoundAssign(CallInstruction setterCall, StatementTransformContext context)
{
// callvirt set_Property(ldloc S_1, binary.op(callvirt get_Property(ldloc S_1), value))
// ==> compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value)
var setterValue = setterCall.Arguments.LastOrDefault();
var storeInSetter = setterValue as StLoc;
if (storeInSetter != null) {
// callvirt set_Property(ldloc S_1, stloc v(binary.op(callvirt get_Property(ldloc S_1), value)))
// ==> stloc v(compound.op.new(callvirt(callvirt get_Property(ldloc S_1)), value))
setterValue = storeInSetter.Value;
}
if (!(setterValue is BinaryNumericInstruction binary))
return false;
var getterCall = binary.Left as CallInstruction;
if (!MatchingGetterAndSetterCalls(getterCall, setterCall))
return false;
if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
return false;
context.Step($"Compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
ILInstruction newInst = new CompoundAssignmentInstruction(
binary, getterCall, binary.Right,
getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue);
if (storeInSetter != null) {
storeInSetter.Value = newInst;
newInst = storeInSetter;
context.RequestRerun(); // moving stloc to top-level might trigger inlining
}
setterCall.ReplaceWith(newInst);
return true;
}
/// <code>
/// stloc s(value)
/// stloc l(ldloc s)
/// -->
/// stloc s(stloc l(value))
/// </code>
void TransformInlineAssignmentLocal(Block block, int i)
bool TransformInlineAssignmentLocal(Block block, int i)
{
var inst = block.Instructions[i] as StLoc;
var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
if (inst == null || nextInst == null)
return;
return false;
if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable))
return;
return false;
context.Step("Inline assignment to local variable", inst);
var value = inst.Value;
var var = nextInst.Variable;
var stackVar = inst.Variable;
block.Instructions.RemoveAt(i);
nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value)));
return true;
}
/// <code>

Loading…
Cancel
Save