Browse Source

Update to SRM 1.6.0 and Roslyn 2.9. Adjust decompiler to new code pattern used for lifted nullable comparisons.

pull/1305/head
Daniel Grunwald 7 years ago
parent
commit
ef866040b4
  1. 6
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 12
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs
  3. 6390
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.opt.roslyn.il
  4. 7088
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.roslyn.il
  5. 203
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il
  6. 201
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il
  7. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  8. 16
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  9. 15
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  10. 3
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  11. 30
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  12. 6
      ILSpy.Tests/ILSpy.Tests.csproj

6
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -37,10 +37,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" /> <PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.8.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.9.0" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="2.8.0" /> <PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="2.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> <PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="NUnit" Version="3.9.0" /> <PackageReference Include="NUnit" Version="3.9.0" />
</ItemGroup> </ItemGroup>

12
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.cs

@ -322,9 +322,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
if (a == b) { if (a == b) {
Console.WriteLine(); Console.WriteLine();
} }
#if ROSLYN
// Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b'
if (!(a == b)) {
Console.WriteLine();
}
#else
if (a != b) { if (a != b) {
Console.WriteLine(); Console.WriteLine();
} }
#endif
if (a > b) { if (a > b) {
Console.WriteLine(); Console.WriteLine();
} }
@ -399,7 +406,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void NumberValueBasic(decimal? a, decimal? b) public static void NumberValueBasic(decimal? a, decimal? b)
{ {
Console.WriteLine(a == b); Console.WriteLine(a == b);
#if ROSLYN
// Roslyn 2.9 started invoking op_Equality even if the source code says 'a != b'
Console.WriteLine(!(a == b));
#else
Console.WriteLine(a != b); Console.WriteLine(a != b);
#endif
Console.WriteLine(a > b); Console.WriteLine(a > b);
Console.WriteLine(!(a > b)); Console.WriteLine(!(a > b));

6390
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.opt.roslyn.il

File diff suppressed because it is too large Load Diff

7088
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LiftedOperators.roslyn.il

File diff suppressed because it is too large Load Diff

203
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.il

@ -2938,7 +2938,7 @@
NestedForeach(class [mscorlib]System.Collections.Generic.List`1<object> items1, NestedForeach(class [mscorlib]System.Collections.Generic.List`1<object> items1,
class [mscorlib]System.Collections.Generic.List`1<object> items2) cil managed class [mscorlib]System.Collections.Generic.List`1<object> items2) cil managed
{ {
// Code size 150 (0x96) // Code size 152 (0x98)
.maxstack 2 .maxstack 2
.locals init (object V_0, .locals init (object V_0,
bool V_1, bool V_1,
@ -2953,7 +2953,7 @@
IL_0008: stloc.3 IL_0008: stloc.3
.try .try
{ {
IL_0009: br.s IL_006b IL_0009: br.s IL_006d
IL_000b: ldloca.s V_3 IL_000b: ldloca.s V_3
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current() IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current()
@ -3009,47 +3009,49 @@
IL_005c: ldloc.1 IL_005c: ldloc.1
IL_005d: stloc.s V_5 IL_005d: stloc.s V_5
IL_005f: ldloc.s V_5 IL_005f: ldloc.s V_5
IL_0061: brtrue.s IL_006a IL_0061: brtrue.s IL_006c
IL_0063: ldloc.0 IL_0063: nop
IL_0064: call void [mscorlib]System.Console::WriteLine(object) IL_0064: ldloc.0
IL_0069: nop IL_0065: call void [mscorlib]System.Console::WriteLine(object)
IL_006a: nop IL_006a: nop
IL_006b: ldloca.s V_3 IL_006b: nop
IL_006d: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext() IL_006c: nop
IL_0072: stloc.s V_5 IL_006d: ldloca.s V_3
IL_0074: ldloc.s V_5 IL_006f: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext()
IL_0076: brtrue.s IL_000b IL_0074: stloc.s V_5
IL_0076: ldloc.s V_5
IL_0078: brtrue.s IL_000b
IL_0078: leave.s IL_0089 IL_007a: leave.s IL_008b
} // end .try } // end .try
finally finally
{ {
IL_007a: ldloca.s V_3 IL_007c: ldloca.s V_3
IL_007c: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> IL_007e: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>
IL_0082: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0084: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0087: nop IL_0089: nop
IL_0088: endfinally IL_008a: endfinally
} // end handler } // end handler
IL_0089: nop IL_008b: nop
IL_008a: ldstr "end" IL_008c: ldstr "end"
IL_008f: call void [mscorlib]System.Console::WriteLine(string) IL_0091: call void [mscorlib]System.Console::WriteLine(string)
IL_0094: nop IL_0096: nop
IL_0095: ret IL_0097: ret
} // end of method Loops::NestedForeach } // end of method Loops::NestedForeach
.method public hidebysig instance void .method public hidebysig instance void
MergeAroundContinue() cil managed MergeAroundContinue() cil managed
{ {
// Code size 137 (0x89) // Code size 140 (0x8c)
.maxstack 2 .maxstack 2
.locals init (int32 V_0, .locals init (int32 V_0,
bool V_1) bool V_1)
IL_0000: nop IL_0000: nop
IL_0001: ldc.i4.0 IL_0001: ldc.i4.0
IL_0002: stloc.0 IL_0002: stloc.0
IL_0003: br.s IL_0074 IL_0003: br.s IL_0077
IL_0005: nop IL_0005: nop
IL_0006: ldloc.0 IL_0006: ldloc.0
@ -3061,7 +3063,7 @@
IL_000d: ceq IL_000d: ceq
IL_000f: stloc.1 IL_000f: stloc.1
IL_0010: ldloc.1 IL_0010: ldloc.1
IL_0011: brtrue.s IL_0021 IL_0011: brtrue.s IL_0022
IL_0013: nop IL_0013: nop
IL_0014: ldloc.0 IL_0014: ldloc.0
@ -3069,94 +3071,97 @@
IL_0016: ceq IL_0016: ceq
IL_0018: stloc.1 IL_0018: stloc.1
IL_0019: ldloc.1 IL_0019: ldloc.1
IL_001a: brtrue.s IL_001e IL_001a: brtrue.s IL_001f
IL_001c: br.s IL_0070 IL_001c: nop
IL_001d: br.s IL_0073
IL_001e: nop IL_001f: nop
IL_001f: br.s IL_0068 IL_0020: br.s IL_006b
IL_0021: ldloc.0 IL_0022: ldloc.0
IL_0022: ldc.i4.5 IL_0023: ldc.i4.5
IL_0023: rem IL_0024: rem
IL_0024: ldc.i4.0 IL_0025: ldc.i4.0
IL_0025: ceq IL_0026: ceq
IL_0027: ldc.i4.0 IL_0028: ldc.i4.0
IL_0028: ceq IL_0029: ceq
IL_002a: stloc.1 IL_002b: stloc.1
IL_002b: ldloc.1 IL_002c: ldloc.1
IL_002c: brtrue.s IL_003c IL_002d: brtrue.s IL_003e
IL_002e: nop
IL_002f: ldloc.0
IL_0030: ldc.i4.5
IL_0031: ceq
IL_0033: stloc.1
IL_0034: ldloc.1
IL_0035: brtrue.s IL_0039
IL_0037: br.s IL_0070 IL_002f: nop
IL_0030: ldloc.0
IL_0031: ldc.i4.5
IL_0032: ceq
IL_0034: stloc.1
IL_0035: ldloc.1
IL_0036: brtrue.s IL_003b
IL_0039: nop IL_0038: nop
IL_003a: br.s IL_0068 IL_0039: br.s IL_0073
IL_003c: ldloc.0 IL_003b: nop
IL_003d: ldc.i4.7 IL_003c: br.s IL_006b
IL_003e: rem
IL_003f: ldc.i4.0
IL_0040: ceq
IL_0042: ldc.i4.0
IL_0043: ceq
IL_0045: stloc.1
IL_0046: ldloc.1
IL_0047: brtrue.s IL_0057
IL_0049: nop IL_003e: ldloc.0
IL_004a: ldloc.0 IL_003f: ldc.i4.7
IL_004b: ldc.i4.7 IL_0040: rem
IL_004c: ceq IL_0041: ldc.i4.0
IL_004e: stloc.1 IL_0042: ceq
IL_004f: ldloc.1 IL_0044: ldc.i4.0
IL_0050: brtrue.s IL_0054 IL_0045: ceq
IL_0047: stloc.1
IL_0048: ldloc.1
IL_0049: brtrue.s IL_005a
IL_0052: br.s IL_0070 IL_004b: nop
IL_004c: ldloc.0
IL_004d: ldc.i4.7
IL_004e: ceq
IL_0050: stloc.1
IL_0051: ldloc.1
IL_0052: brtrue.s IL_0057
IL_0054: nop IL_0054: nop
IL_0055: br.s IL_0068 IL_0055: br.s IL_0073
IL_0057: nop
IL_0058: br.s IL_006b
IL_0057: ldloc.0 IL_005a: ldloc.0
IL_0058: ldc.i4.s 11 IL_005b: ldc.i4.s 11
IL_005a: rem IL_005d: rem
IL_005b: ldc.i4.0
IL_005c: ceq
IL_005e: ldc.i4.0 IL_005e: ldc.i4.0
IL_005f: ceq IL_005f: ceq
IL_0061: stloc.1 IL_0061: ldc.i4.0
IL_0062: ldloc.1 IL_0062: ceq
IL_0063: brtrue.s IL_0068 IL_0064: stloc.1
IL_0065: ldloc.1
IL_0066: brtrue.s IL_006b
IL_0065: nop IL_0068: nop
IL_0066: br.s IL_0070 IL_0069: br.s IL_0073
IL_0068: ldloc.0 IL_006b: ldloc.0
IL_0069: call void [mscorlib]System.Console::WriteLine(int32) IL_006c: call void [mscorlib]System.Console::WriteLine(int32)
IL_006e: nop IL_0071: nop
IL_006f: nop IL_0072: nop
IL_0070: ldloc.0 IL_0073: ldloc.0
IL_0071: ldc.i4.1 IL_0074: ldc.i4.1
IL_0072: add IL_0075: add
IL_0073: stloc.0 IL_0076: stloc.0
IL_0074: ldloc.0 IL_0077: ldloc.0
IL_0075: ldc.i4.s 20 IL_0078: ldc.i4.s 20
IL_0077: clt IL_007a: clt
IL_0079: stloc.1 IL_007c: stloc.1
IL_007a: ldloc.1 IL_007d: ldloc.1
IL_007b: brtrue.s IL_0005 IL_007e: brtrue.s IL_0005
IL_007d: ldstr "end" IL_0080: ldstr "end"
IL_0082: call void [mscorlib]System.Console::WriteLine(string) IL_0085: call void [mscorlib]System.Console::WriteLine(string)
IL_0087: nop IL_008a: nop
IL_0088: ret IL_008b: ret
} // end of method Loops::MergeAroundContinue } // end of method Loops::MergeAroundContinue
.method public hidebysig specialname rtspecialname .method public hidebysig specialname rtspecialname

201
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.roslyn.il

@ -2756,7 +2756,7 @@
NestedForeach(class [mscorlib]System.Collections.Generic.List`1<object> items1, NestedForeach(class [mscorlib]System.Collections.Generic.List`1<object> items1,
class [mscorlib]System.Collections.Generic.List`1<object> items2) cil managed class [mscorlib]System.Collections.Generic.List`1<object> items2) cil managed
{ {
// Code size 141 (0x8d) // Code size 143 (0x8f)
.maxstack 2 .maxstack 2
.locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> V_0, .locals init (valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> V_0,
object V_1, object V_1,
@ -2772,7 +2772,7 @@
IL_0008: stloc.0 IL_0008: stloc.0
.try .try
{ {
IL_0009: br.s IL_0067 IL_0009: br.s IL_0069
IL_000b: ldloca.s V_0 IL_000b: ldloca.s V_0
IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current() IL_000d: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::get_Current()
@ -2825,37 +2825,39 @@
IL_0057: ceq IL_0057: ceq
IL_0059: stloc.s V_6 IL_0059: stloc.s V_6
IL_005b: ldloc.s V_6 IL_005b: ldloc.s V_6
IL_005d: brfalse.s IL_0066 IL_005d: brfalse.s IL_0068
IL_005f: ldloc.1 IL_005f: nop
IL_0060: call void [mscorlib]System.Console::WriteLine(object) IL_0060: ldloc.1
IL_0065: nop IL_0061: call void [mscorlib]System.Console::WriteLine(object)
IL_0066: nop IL_0066: nop
IL_0067: ldloca.s V_0 IL_0067: nop
IL_0069: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext() IL_0068: nop
IL_006e: brtrue.s IL_000b IL_0069: ldloca.s V_0
IL_006b: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>::MoveNext()
IL_0070: brtrue.s IL_000b
IL_0070: leave.s IL_0081 IL_0072: leave.s IL_0083
} // end .try } // end .try
finally finally
{ {
IL_0072: ldloca.s V_0 IL_0074: ldloca.s V_0
IL_0074: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object> IL_0076: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<object>
IL_007a: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_007c: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_007f: nop IL_0081: nop
IL_0080: endfinally IL_0082: endfinally
} // end handler } // end handler
IL_0081: ldstr "end" IL_0083: ldstr "end"
IL_0086: call void [mscorlib]System.Console::WriteLine(string) IL_0088: call void [mscorlib]System.Console::WriteLine(string)
IL_008b: nop IL_008d: nop
IL_008c: ret IL_008e: ret
} // end of method Loops::NestedForeach } // end of method Loops::NestedForeach
.method public hidebysig instance void .method public hidebysig instance void
MergeAroundContinue() cil managed MergeAroundContinue() cil managed
{ {
// Code size 144 (0x90) // Code size 150 (0x96)
.maxstack 2 .maxstack 2
.locals init (int32 V_0, .locals init (int32 V_0,
bool V_1, bool V_1,
@ -2869,7 +2871,7 @@
IL_0000: nop IL_0000: nop
IL_0001: ldc.i4.0 IL_0001: ldc.i4.0
IL_0002: stloc.0 IL_0002: stloc.0
IL_0003: br.s IL_0079 IL_0003: br.s IL_007c
IL_0005: nop IL_0005: nop
IL_0006: ldloc.0 IL_0006: ldloc.0
@ -2879,7 +2881,7 @@
IL_000a: ceq IL_000a: ceq
IL_000c: stloc.1 IL_000c: stloc.1
IL_000d: ldloc.1 IL_000d: ldloc.1
IL_000e: brfalse.s IL_0021 IL_000e: brfalse.s IL_0022
IL_0010: nop IL_0010: nop
IL_0011: ldloc.0 IL_0011: ldloc.0
@ -2889,92 +2891,95 @@
IL_0016: ceq IL_0016: ceq
IL_0018: stloc.2 IL_0018: stloc.2
IL_0019: ldloc.2 IL_0019: ldloc.2
IL_001a: brfalse.s IL_001e IL_001a: brfalse.s IL_001f
IL_001c: br.s IL_0075 IL_001c: nop
IL_001d: br.s IL_0078
IL_001e: nop IL_001f: nop
IL_001f: br.s IL_006d IL_0020: br.s IL_0070
IL_0021: ldloc.0 IL_0022: ldloc.0
IL_0022: ldc.i4.5 IL_0023: ldc.i4.5
IL_0023: rem IL_0024: rem
IL_0024: ldc.i4.0 IL_0025: ldc.i4.0
IL_0025: ceq IL_0026: ceq
IL_0027: stloc.3 IL_0028: stloc.3
IL_0028: ldloc.3 IL_0029: ldloc.3
IL_0029: brfalse.s IL_003e IL_002a: brfalse.s IL_0040
IL_002b: nop IL_002c: nop
IL_002c: ldloc.0 IL_002d: ldloc.0
IL_002d: ldc.i4.5 IL_002e: ldc.i4.5
IL_002e: ceq IL_002f: ceq
IL_0030: ldc.i4.0 IL_0031: ldc.i4.0
IL_0031: ceq IL_0032: ceq
IL_0033: stloc.s V_4 IL_0034: stloc.s V_4
IL_0035: ldloc.s V_4 IL_0036: ldloc.s V_4
IL_0037: brfalse.s IL_003b IL_0038: brfalse.s IL_003d
IL_0039: br.s IL_0075 IL_003a: nop
IL_003b: br.s IL_0078
IL_003b: nop IL_003d: nop
IL_003c: br.s IL_006d IL_003e: br.s IL_0070
IL_003e: ldloc.0 IL_0040: ldloc.0
IL_003f: ldc.i4.7 IL_0041: ldc.i4.7
IL_0040: rem IL_0042: rem
IL_0041: ldc.i4.0 IL_0043: ldc.i4.0
IL_0042: ceq IL_0044: ceq
IL_0044: stloc.s V_5 IL_0046: stloc.s V_5
IL_0046: ldloc.s V_5 IL_0048: ldloc.s V_5
IL_0048: brfalse.s IL_005d IL_004a: brfalse.s IL_0060
IL_004a: nop IL_004c: nop
IL_004b: ldloc.0 IL_004d: ldloc.0
IL_004c: ldc.i4.7 IL_004e: ldc.i4.7
IL_004d: ceq IL_004f: ceq
IL_004f: ldc.i4.0 IL_0051: ldc.i4.0
IL_0050: ceq IL_0052: ceq
IL_0052: stloc.s V_6 IL_0054: stloc.s V_6
IL_0054: ldloc.s V_6 IL_0056: ldloc.s V_6
IL_0056: brfalse.s IL_005a IL_0058: brfalse.s IL_005d
IL_0058: br.s IL_0075
IL_005a: nop IL_005a: nop
IL_005b: br.s IL_006d IL_005b: br.s IL_0078
IL_005d: ldloc.0 IL_005d: nop
IL_005e: ldc.i4.s 11 IL_005e: br.s IL_0070
IL_0060: rem
IL_0061: ldc.i4.0
IL_0062: ceq
IL_0064: stloc.s V_7
IL_0066: ldloc.s V_7
IL_0068: brfalse.s IL_006d
IL_006a: nop IL_0060: ldloc.0
IL_006b: br.s IL_0075 IL_0061: ldc.i4.s 11
IL_0063: rem
IL_006d: ldloc.0 IL_0064: ldc.i4.0
IL_006e: call void [mscorlib]System.Console::WriteLine(int32) IL_0065: ceq
IL_0073: nop IL_0067: stloc.s V_7
IL_0074: nop IL_0069: ldloc.s V_7
IL_0075: ldloc.0 IL_006b: brfalse.s IL_0070
IL_0076: ldc.i4.1
IL_0077: add IL_006d: nop
IL_0078: stloc.0 IL_006e: br.s IL_0078
IL_0079: ldloc.0
IL_007a: ldc.i4.s 20 IL_0070: ldloc.0
IL_007c: clt IL_0071: call void [mscorlib]System.Console::WriteLine(int32)
IL_007e: stloc.s V_8 IL_0076: nop
IL_0080: ldloc.s V_8 IL_0077: nop
IL_0082: brtrue.s IL_0005 IL_0078: ldloc.0
IL_0079: ldc.i4.1
IL_0084: ldstr "end" IL_007a: add
IL_0089: call void [mscorlib]System.Console::WriteLine(string) IL_007b: stloc.0
IL_008e: nop IL_007c: ldloc.0
IL_008f: ret IL_007d: ldc.i4.s 20
IL_007f: clt
IL_0081: stloc.s V_8
IL_0083: ldloc.s V_8
IL_0085: brtrue IL_0005
IL_008a: ldstr "end"
IL_008f: call void [mscorlib]System.Console::WriteLine(string)
IL_0094: nop
IL_0095: ret
} // end of method Loops::MergeAroundContinue } // end of method Loops::MergeAroundContinue
.method public hidebysig specialname rtspecialname .method public hidebysig specialname rtspecialname

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -51,8 +51,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.2.0" /> <PackageReference Include="Humanizer.Core" Version="2.2.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> <PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Reflection.Metadata" Version="1.4.2" /> <PackageReference Include="System.Reflection.Metadata" Version="1.6.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" /> <PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup> </ItemGroup>

16
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -177,6 +177,22 @@ namespace ICSharpCode.Decompiler.IL
default: default:
return SpecialType.UnknownType; return SpecialType.UnknownType;
} }
case BinaryNumericInstruction bni:
if (bni.IsLifted)
return SpecialType.UnknownType;
switch (bni.Operator) {
case BinaryNumericOperator.BitAnd:
case BinaryNumericOperator.BitOr:
case BinaryNumericOperator.BitXor:
var left = bni.Left.InferType(compilation);
var right = bni.Right.InferType(compilation);
if (left.Equals(right) && (left.IsCSharpPrimitiveIntegerType() || left.IsKnownType(KnownTypeCode.Boolean)))
return left;
else
return SpecialType.UnknownType;
default:
return SpecialType.UnknownType;
}
default: default:
return SpecialType.UnknownType; return SpecialType.UnknownType;
} }

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

@ -499,13 +499,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break; break;
case BinaryNumericOperator.BitAnd: case BinaryNumericOperator.BitAnd:
if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) if (inst.Left.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean)
&& inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean) && inst.Right.InferType(context.TypeSystem).IsKnownType(KnownTypeCode.Boolean))
&& SemanticHelper.IsPure(inst.Right.Flags))
{ {
context.Step("Replace bit.and with logic.and", inst); if (new NullableLiftingTransform(context).Run(inst)) {
var expr = IfInstruction.LogicAnd(inst.Left, inst.Right); // e.g. "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)"
inst.ReplaceWith(expr); } else if (SemanticHelper.IsPure(inst.Right.Flags)) {
expr.AcceptVisitor(this); context.Step("Replace bit.and with logic.and", inst);
var expr = IfInstruction.LogicAnd(inst.Left, inst.Right);
inst.ReplaceWith(expr);
expr.AcceptVisitor(this);
}
} }
break; break;
} }

3
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -39,7 +39,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// We exclude logic.and to avoid turning // We exclude logic.and to avoid turning
// "logic.and(comp(interfaces != ldnull), call get_Count(interfaces))" // "logic.and(comp(interfaces != ldnull), call get_Count(interfaces))"
// into "if ((interfaces?.Count ?? 0) != 0)". // into "if ((interfaces?.Count ?? 0) != 0)".
return (ifInst.MatchLogicAnd(out _, out _) || ifInst.MatchLogicOr(out _, out _)) return ifInst != null
&& (ifInst.MatchLogicAnd(out _, out _) || ifInst.MatchLogicOr(out _, out _))
&& IfInstruction.IsInConditionSlot(ifInst); && IfInstruction.IsInConditionSlot(ifInst);
} }

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

@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary> /// </summary>
public bool Run(IfInstruction ifInst) public bool Run(IfInstruction ifInst)
{ {
var lifted = Lift(ifInst, ifInst.TrueInst, ifInst.FalseInst); var lifted = Lift(ifInst, ifInst.Condition, ifInst.TrueInst, ifInst.FalseInst);
if (lifted != null) { if (lifted != null) {
ifInst.ReplaceWith(lifted); ifInst.ReplaceWith(lifted);
return true; return true;
@ -64,6 +64,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
} }
/// <summary>
/// VS2017.8 / Roslyn 2.9 started optimizing some cases of
/// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)"
/// to
/// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)"
/// so this secondary entry point analyses logic.and as-if it was a short-circuting &&.
/// </summary>
public bool Run(BinaryNumericInstruction bni)
{
Debug.Assert(!bni.IsLifted && bni.Operator == BinaryNumericOperator.BitAnd);
// caller ensures that bni.Left/bni.Right are booleans
var lifted = Lift(bni, bni.Left, bni.Right, new LdcI4(0));
if (lifted != null) {
bni.ReplaceWith(lifted);
return true;
}
return false;
}
public bool RunStatements(Block block, int pos) public bool RunStatements(Block block, int pos)
{ {
/// e.g.: /// e.g.:
@ -85,7 +104,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
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, ifInst.Condition, thenLeave.Value, elseLeave.Value);
if (lifted != null) { if (lifted != null) {
thenLeave.Value = lifted; thenLeave.Value = lifted;
ifInst.ReplaceWith(thenLeave); ifInst.ReplaceWith(thenLeave);
@ -118,14 +137,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Main entry point for lifting; called by both the expression-transform /// Main entry point for lifting; called by both the expression-transform
/// and the block transform. /// and the block transform.
/// </summary> /// </summary>
ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst) ILInstruction Lift(ILInstruction ifInst, ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst)
{ {
ILInstruction condition = ifInst.Condition; // ifInst is usually the IfInstruction to which condition belongs;
// but can also be a BinaryNumericInstruction.
while (condition.MatchLogicNot(out var arg)) { while (condition.MatchLogicNot(out var arg)) {
condition = arg; condition = arg;
ExtensionMethods.Swap(ref trueInst, ref falseInst); ExtensionMethods.Swap(ref trueInst, ref falseInst);
} }
if (context.Settings.NullPropagation && !NullPropagationTransform.IsProtectedIfInst(ifInst)) { if (context.Settings.NullPropagation && !NullPropagationTransform.IsProtectedIfInst(ifInst as IfInstruction)) {
var nullPropagated = new NullPropagationTransform(context) var nullPropagated = new NullPropagationTransform(context)
.Run(condition, trueInst, falseInst, ifInst.ILRange); .Run(condition, trueInst, falseInst, ifInst.ILRange);
if (nullPropagated != null) if (nullPropagated != null)

6
ILSpy.Tests/ILSpy.Tests.csproj

@ -42,10 +42,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" /> <PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.8.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.9.0" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="2.8.0" /> <PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="2.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> <PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="NUnit" Version="3.9.0" /> <PackageReference Include="NUnit" Version="3.9.0" />
</ItemGroup> </ItemGroup>

Loading…
Cancel
Save