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 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DiffLib" Version="1.0.0.55" /> <PackageReference Include="DiffLib" Version="1.0.0.55" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="1.3.2" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.3.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.0" /> <PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="NUnit" Version="2.6.3" /> <PackageReference Include="NUnit" Version="2.6.3" />
<PackageReference Include="NUnitTestAdapter" Version="2.0.0" /> <PackageReference Include="NUnitTestAdapter" Version="2.0.0" />
</ItemGroup> </ItemGroup>

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

@ -26,6 +26,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static void Main() static void Main()
{ {
PreIncrementProperty(); PreIncrementProperty();
PreIncrementIndexer();
CallTwice();
UnsignedShiftRightInstanceField();
UnsignedShiftRightStaticProperty();
} }
static void Test(int a, int b) static void Test(int a, int b)
@ -41,16 +45,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
return ++x; return ++x;
} }
static int instanceCount;
int instanceNumber = ++instanceCount;
int instanceField; int instanceField;
public int InstanceProperty public int InstanceProperty
{ {
get { get {
Console.WriteLine("In get_InstanceProperty"); Console.WriteLine("In {0}.get_InstanceProperty", instanceNumber);
return instanceField; return instanceField;
} }
set { set {
Console.WriteLine("In set_InstanceProperty, value=" + value); Console.WriteLine("In {0}.set_InstanceProperty, value=" + value, instanceNumber);
instanceField = value; instanceField = value;
} }
} }
@ -72,7 +79,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static Dictionary<string, int> GetDict() public static Dictionary<string, int> GetDict()
{ {
Console.WriteLine("In 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() static string GetString()
@ -93,5 +106,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine("PreIncrementIndexer:"); Console.WriteLine("PreIncrementIndexer:");
Test(X(), ++GetDict()[GetString()]); 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
{ {
return --array[pos]; 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() public int PreIncrementInstanceField()
{ {
return ++this.M().Field; return ++this.M().Field;
} }
//public int PostIncrementInstanceField()
//{
// return this.M().Field++;
//}
public void IncrementInstanceField()
{
this.M().Field++;
}
public int PreIncrementInstanceField2(MutableClass m) public int PreIncrementInstanceField2(MutableClass m)
{ {
return ++m.Field; return ++m.Field;
} }
//public int PostIncrementInstanceField2(MutableClass m)
//{
// return m.Field++;
//}
public void IncrementInstanceField2(MutableClass m)
{
m.Field++;
}
public int PreIncrementInstanceProperty() public int PreIncrementInstanceProperty()
{ {
return ++this.M().Property; return ++this.M().Property;
} }
//public int PostIncrementInstanceProperty()
//{
// return this.M().Property++;
//}
public void IncrementInstanceProperty()
{
this.M().Property++;
}
public int PreIncrementStaticField() public int PreIncrementStaticField()
{ {
return ++CompoundAssignmentTest.StaticField; return ++CompoundAssignmentTest.StaticField;
} }
public int PostIncrementStaticField()
{
return CompoundAssignmentTest.StaticField++;
}
public void IncrementStaticField()
{
CompoundAssignmentTest.StaticField++;
}
public int PreIncrementStaticProperty() public int PreIncrementStaticProperty()
{ {
return ++CompoundAssignmentTest.StaticProperty; return ++CompoundAssignmentTest.StaticProperty;
} }
//public int PostIncrementStaticProperty()
//{
// return CompoundAssignmentTest.StaticProperty++;
//}
public void IncrementStaticProperty()
{
CompoundAssignmentTest.StaticProperty++;
}
private static Item GetItem(object obj) private static Item GetItem(object obj)
{ {
return null; return null;

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

@ -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. // Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0 .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.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 .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@
.hash algorithm 0x00008004 .hash algorithm 0x00008004
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module wswrh1ww.dll .module kiryblux.dll
// MVID: {0FA9B26D-B874-4697-B526-76EF6D2DAD90} // MVID: {34EC794D-1FFD-488F-9BDC-2D5D9C15854D}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x029E0000 // Image base: 0x02A30000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -762,6 +762,51 @@
IL_001c: ret IL_001c: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement } // 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 .method public hidebysig instance int32
PreIncrementInstanceField() cil managed PreIncrementInstanceField() cil managed
{ {
@ -787,6 +832,22 @@
IL_001b: ret IL_001b: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField } // 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 .method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{ {
@ -811,6 +872,21 @@
IL_0016: ret IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2 } // 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 .method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed PreIncrementInstanceProperty() cil managed
{ {
@ -837,6 +913,23 @@
IL_001c: ret IL_001c: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty } // 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 .method public hidebysig instance int32
PreIncrementStaticField() cil managed PreIncrementStaticField() cil managed
{ {
@ -856,6 +949,38 @@
IL_0012: ret IL_0012: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField } // 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 .method public hidebysig instance int32
PreIncrementStaticProperty() cil managed PreIncrementStaticProperty() cil managed
{ {
@ -876,6 +1001,20 @@
IL_0013: ret IL_0013: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty } // 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 .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed GetItem(object obj) cil managed
{ {

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

@ -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. // Copyright (c) Microsoft Corporation. All rights reserved.
@ -10,7 +10,7 @@
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0 .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.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 .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
@ -20,15 +20,15 @@
.hash algorithm 0x00008004 .hash algorithm 0x00008004
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module ag42vicc.dll .module d4bhqxbe.dll
// MVID: {EB752380-6A62-4B20-9497-B68A784C5BD7} // MVID: {B884BD61-6FE3-4893-B161-02EE675C0DF0}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x00DE0000 // Image base: 0x003A0000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -647,6 +647,44 @@
IL_0017: ret IL_0017: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement } // 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 .method public hidebysig instance int32
PreIncrementInstanceField() cil managed PreIncrementInstanceField() cil managed
{ {
@ -666,6 +704,21 @@
IL_0016: ret IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField } // 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 .method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{ {
@ -684,6 +737,20 @@
IL_0011: ret IL_0011: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2 } // 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 .method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed PreIncrementInstanceProperty() cil managed
{ {
@ -703,6 +770,21 @@
IL_0016: ret IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty } // 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 .method public hidebysig instance int32
PreIncrementStaticField() cil managed PreIncrementStaticField() cil managed
{ {
@ -716,6 +798,31 @@
IL_000d: ret IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField } // 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 .method public hidebysig instance int32
PreIncrementStaticProperty() cil managed PreIncrementStaticProperty() cil managed
{ {
@ -729,6 +836,18 @@
IL_000d: ret IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty } // 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 .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed GetItem(object obj) cil managed
{ {

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

@ -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. // Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module CompoundAssignmentTest.dll .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 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x008E0000 // Image base: 0x028A0000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -650,6 +650,44 @@
IL_000f: ret IL_000f: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement } // 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 .method public hidebysig instance int32
PreIncrementInstanceField() cil managed PreIncrementInstanceField() cil managed
{ {
@ -669,6 +707,21 @@
IL_0016: ret IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField } // 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 .method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{ {
@ -687,6 +740,20 @@
IL_0011: ret IL_0011: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2 } // 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 .method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed PreIncrementInstanceProperty() cil managed
{ {
@ -706,6 +773,24 @@
IL_0016: ret IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty } // 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 .method public hidebysig instance int32
PreIncrementStaticField() cil managed PreIncrementStaticField() cil managed
{ {
@ -719,6 +804,31 @@
IL_000d: ret IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField } // 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 .method public hidebysig instance int32
PreIncrementStaticProperty() cil managed PreIncrementStaticProperty() cil managed
{ {
@ -732,6 +842,18 @@
IL_000d: ret IL_000d: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty } // 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 .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed GetItem(object obj) cil managed
{ {

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

@ -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. // Copyright (c) Microsoft Corporation. All rights reserved.
@ -25,14 +25,14 @@
.ver 0:0:0:0 .ver 0:0:0:0
} }
.module CompoundAssignmentTest.dll .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 ) .custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000 .imagebase 0x10000000
.file alignment 0x00000200 .file alignment 0x00000200
.stackreserve 0x00100000 .stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI .subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY .corflags 0x00000001 // ILONLY
// Image base: 0x00D10000 // Image base: 0x003D0000
// =============== CLASS MEMBERS DECLARATION =================== // =============== CLASS MEMBERS DECLARATION ===================
@ -759,6 +759,51 @@
IL_0014: ret IL_0014: ret
} // end of method CompoundAssignmentTest::PreIncrementArrayElement } // 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 .method public hidebysig instance int32
PreIncrementInstanceField() cil managed PreIncrementInstanceField() cil managed
{ {
@ -784,6 +829,22 @@
IL_001b: ret IL_001b: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField } // 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 .method public hidebysig instance int32
PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed PreIncrementInstanceField2(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/MutableClass m) cil managed
{ {
@ -808,6 +869,21 @@
IL_0016: ret IL_0016: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceField2 } // 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 .method public hidebysig instance int32
PreIncrementInstanceProperty() cil managed PreIncrementInstanceProperty() cil managed
{ {
@ -834,6 +910,26 @@
IL_001c: ret IL_001c: ret
} // end of method CompoundAssignmentTest::PreIncrementInstanceProperty } // 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 .method public hidebysig instance int32
PreIncrementStaticField() cil managed PreIncrementStaticField() cil managed
{ {
@ -853,6 +949,38 @@
IL_0012: ret IL_0012: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticField } // 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 .method public hidebysig instance int32
PreIncrementStaticProperty() cil managed PreIncrementStaticProperty() cil managed
{ {
@ -873,6 +1001,20 @@
IL_0013: ret IL_0013: ret
} // end of method CompoundAssignmentTest::PreIncrementStaticProperty } // 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 .method private hidebysig static class ICSharpCode.Decompiler.Tests.TestCases.Pretty.CompoundAssignmentTest/Item
GetItem(object obj) cil managed GetItem(object obj) cil managed
{ {

12
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

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

5
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

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

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

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

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

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

@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.IL
public CompoundAssignmentInstruction(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType) public CompoundAssignmentInstruction(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType)
: base(OpCode.CompoundAssignmentInstruction) : base(OpCode.CompoundAssignmentInstruction)
{ {
Debug.Assert(IsBinaryCompatibleWithType(binary, type));
this.CheckForOverflow = binary.CheckForOverflow; this.CheckForOverflow = binary.CheckForOverflow;
this.Sign = binary.Sign; this.Sign = binary.Sign;
this.LeftInputType = binary.LeftInputType; this.LeftInputType = binary.LeftInputType;
@ -69,10 +70,28 @@ namespace ICSharpCode.Decompiler.IL
this.Target = target; this.Target = target;
this.type = type; this.type = type;
this.Value = value; this.Value = value;
this.ILRange = binary.ILRange;
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(IsValidCompoundAssignmentTarget(Target)); 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) internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst)
{ {
switch (inst.OpCode) { switch (inst.OpCode) {

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

@ -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 @@
// 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 // 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 // software and associated documentation files (the "Software"), to deal in the Software
@ -29,16 +29,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <remarks> /// <remarks>
/// Should run after inlining so that the expression patterns can be detected. /// Should run after inlining so that the expression patterns can be detected.
/// </remarks> /// </remarks>
public class ExpressionTransforms : ILVisitor, IBlockTransform, IStatementTransform public class ExpressionTransforms : ILVisitor, IStatementTransform
{ {
internal ILTransformContext context; internal StatementTransformContext context;
public void Run(Block block, BlockTransformContext context)
{
this.context = context;
Default(block);
}
public void Run(Block block, int pos, StatementTransformContext context) public void Run(Block block, int pos, StatementTransformContext context)
{ {
this.context = context; this.context = context;
@ -229,23 +223,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitCall(Call inst) protected internal override void VisitCall(Call inst)
{ {
if (inst.Method.IsConstructor && !inst.Method.IsStatic && inst.Method.DeclaringType.Kind == TypeKind.Struct) { var expr = EarlyExpressionTransforms.HandleCall(inst, context);
Debug.Assert(inst.Arguments.Count == inst.Method.Parameters.Count + 1); if (expr != null) {
context.Step("Transform call to struct constructor", inst); // The resulting expression may trigger further rules, so continue visiting the replacement:
// call(ref, ...) expr.AcceptVisitor(this);
// => 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);
} else { } else {
base.VisitCall(inst); 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) protected internal override void VisitNewObj(NewObj inst)
{ {
LdcDecimal decimalConstant; LdcDecimal decimalConstant;
@ -286,13 +279,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStObj(StObj inst) protected internal override void VisitStObj(StObj inst)
{ {
base.VisitStObj(inst); base.VisitStObj(inst);
if (StObjToStLoc(inst, context)) { if (EarlyExpressionTransforms.StObjToStLoc(inst, context)) {
context.RequestRerun();
return; return;
} }
if (inst.Value is BinaryNumericInstruction binary if (inst.Value is BinaryNumericInstruction binary
&& binary.Left.MatchLdObj(out ILInstruction target, out IType t) && 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); context.Step("compound assignment", inst);
// stobj(target, binary.op(ldobj(target), ...)) // stobj(target, binary.op(ldobj(target), ...))
@ -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) protected internal override void VisitIfInstruction(IfInstruction inst)
{ {
inst.TrueInst.AcceptVisitor(this); inst.TrueInst.AcceptVisitor(this);
@ -360,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1); var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1);
newIf.ILRange = inst.ILRange; newIf.ILRange = inst.ILRange;
inst.ReplaceWith(new StLoc(v, newIf)); 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 newIf;
} }
return inst; return inst;

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

@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
public bool RunBlock(Block block) public bool RunStatements(Block block, int pos)
{ {
if (!context.Settings.LiftNullables) if (!context.Settings.LiftNullables)
return false; return false;
@ -73,18 +73,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // leave IL_0000 (default.value System.Nullable`1[[System.Int64]])
// } // }
// leave IL_0000 (newobj .ctor(exprToLift)) // leave IL_0000 (newobj .ctor(exprToLift))
IfInstruction ifInst; if (pos != block.Instructions.Count - 2)
if (block.Instructions.Last() is Leave elseLeave) { return false;
ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; if (!(block.Instructions[pos] is IfInstruction ifInst))
if (ifInst == null || !ifInst.FalseInst.MatchNop())
return false;
} else {
return false; return false;
}
if (!(Block.Unwrap(ifInst.TrueInst) is Leave thenLeave)) if (!(Block.Unwrap(ifInst.TrueInst) is Leave thenLeave))
return false; return false;
if (!ifInst.FalseInst.MatchNop())
return false;
if (!(block.Instructions[pos + 1] is Leave elseLeave))
return false;
if (elseLeave.TargetContainer != thenLeave.TargetContainer) if (elseLeave.TargetContainer != thenLeave.TargetContainer)
return false; return false;
var lifted = Lift(ifInst, thenLeave.Value, elseLeave.Value); var lifted = Lift(ifInst, thenLeave.Value, elseLeave.Value);
if (lifted != null) { if (lifted != null) {
thenLeave.Value = lifted; thenLeave.Value = lifted;
@ -149,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// => a != b // => a != b
return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst) return LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst)
?? LiftCSharpUserEqualityComparison(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) // (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
// => Activator.CreateInstance<T>() // => Activator.CreateInstance<T>()
return trueInst; return trueInst;
@ -245,16 +247,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null; 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) // (default(T) == null) ? Activator.CreateInstance<T>() : default(T)
return falseInst.MatchDefaultValue(out var type) && return falseInst.MatchDefaultValue(out var type) &&
(trueInst is Call c && c.Method.FullName == "System.Activator.CreateInstance" && c.Method.TypeArguments.Count == 1) && (trueInst is Call c && c.Method.FullName == "System.Activator.CreateInstance" && c.Method.TypeArguments.Count == 1) &&
type.Kind == TypeKind.TypeParameter && type.Kind == TypeKind.TypeParameter &&
condition.MatchCompEquals(out var left, out var right) && compLeft.MatchDefaultValue(out var type2) &&
left.MatchDefaultValue(out var type2) &&
type.Equals(type2) && type.Equals(type2) &&
right.MatchLdNull(); compRight.MatchLdNull();
} }
private bool MatchThreeValuedLogicConditionPattern(ILInstruction condition, out ILVariable nullable1, out ILVariable nullable2) private bool MatchThreeValuedLogicConditionPattern(ILInstruction condition, out ILVariable nullable1, out ILVariable nullable2)
@ -847,11 +848,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
#endregion #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 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
/// <summary> /// <summary>
/// Description of TransformAssignment. /// Constructs compound assignments and inline assignments.
/// </summary> /// </summary>
public class TransformAssignment : IBlockTransform public class TransformAssignment : IBlockTransform
{ {
@ -42,7 +43,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
if (TransformInlineAssignmentStObj(block, i)) if (TransformInlineAssignmentStObj(block, i))
continue; continue;
if (TransformInlineAssignmentCall(block, i)) if (TransformInlineCompoundAssignmentCall(block, i))
continue;
if (TransformRoslynCompoundAssignmentCall(block, i))
continue; continue;
} }
} }
@ -82,8 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
} else { // otherwise it must be local } else { // otherwise it must be local
TransformInlineAssignmentLocal(block, i); return TransformInlineAssignmentLocal(block, i);
return false;
} }
context.Step("Inline assignment to instance field", fieldStore); context.Step("Inline assignment to instance field", fieldStore);
local = localStore.Variable; local = localStore.Variable;
@ -106,76 +108,151 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <code> /// <code>
/// stloc s(binary(callvirt(getter), value)) /// stloc s(binary(callvirt(getter), value))
/// callvirt (setter, ldloc s) /// callvirt (setter, ldloc s)
/// ... usage of s ... /// (followed by single usage of s in next instruction)
/// -->
/// ... compound.op.new(callvirt(getter), value) ...
/// </code>
/// -or-
/// <code>
/// stloc s(stloc v(binary(callvirt(getter), value)))
/// callvirt (setter, ldloc s)
/// ... usage of v ...
/// --> /// -->
/// ... compound.op.new(callvirt(getter), value) ... /// stloc s(compound.op.new(callvirt(getter), value))
/// </code> /// </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 // 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; 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 getterCall = binary?.Left as CallInstruction;
var setterCall = block.Instructions.ElementAtOrDefault(i + 1) 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; return false;
var owner = getterCall.Method.AccessorOwner as IProperty; if (!setterCall.Arguments.Last().MatchLdLoc(localVariable))
if (owner == null || !IsSameMember(getterCall.Method, owner.Getter) || !IsSameMember(setterCall.Method, owner.Setter))
return false; return false;
var next = block.Instructions.ElementAtOrDefault(i + 2); var next = block.Instructions.ElementAtOrDefault(i + 2);
if (next == null) if (next == null)
return false; return false;
var usages = next.Descendants.Where(d => d.MatchLdLoc(localVariable)).ToArray(); if (next.Descendants.Where(d => d.MatchLdLoc(localVariable)).Count() != 1)
if (usages.Length != 1)
return false; return false;
context.Step($"Compound assignment to '{owner.Name}'", setterCall); if (!CompoundAssignmentInstruction.IsBinaryCompatibleWithType(binary, getterCall.Method.ReturnType))
block.Instructions.RemoveAt(i + 1); return false;
block.Instructions.RemoveAt(i); context.Step($"Inline compound assignment to '{getterCall.Method.AccessorOwner.Name}'", setterCall);
usages[0].ReplaceWith(new CompoundAssignmentInstruction( block.Instructions.RemoveAt(i + 1); // remove setter call
binary.ReplaceWith(new CompoundAssignmentInstruction(
binary, getterCall, binary.Right, binary, getterCall, binary.Right,
getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue)); getterCall.Method.ReturnType, CompoundAssignmentType.EvaluatesToNewValue));
return true; 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> /// <code>
/// stloc s(value) /// stloc s(value)
/// stloc l(ldloc s) /// stloc l(ldloc s)
/// --> /// -->
/// stloc s(stloc l(value)) /// stloc s(stloc l(value))
/// </code> /// </code>
void TransformInlineAssignmentLocal(Block block, int i) bool TransformInlineAssignmentLocal(Block block, int i)
{ {
var inst = block.Instructions[i] as StLoc; var inst = block.Instructions[i] as StLoc;
var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc; var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc;
if (inst == null || nextInst == null) if (inst == null || nextInst == null)
return; return false;
if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable)) if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable))
return; return false;
context.Step("Inline assignment to local variable", inst); context.Step("Inline assignment to local variable", inst);
var value = inst.Value; var value = inst.Value;
var var = nextInst.Variable; var var = nextInst.Variable;
var stackVar = inst.Variable; var stackVar = inst.Variable;
block.Instructions.RemoveAt(i); block.Instructions.RemoveAt(i);
nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value))); nextInst.ReplaceWith(new StLoc(stackVar, new StLoc(var, value)));
return true;
} }
/// <code> /// <code>

Loading…
Cancel
Save