diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index 27e42e795..4dbda353a 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -54,6 +54,12 @@ namespace ICSharpCode.Decompiler.Tests CompilerOptions.Optimize | CompilerOptions.UseRoslyn }; + static readonly CompilerOptions[] roslynOnlyOptions = + { + CompilerOptions.UseRoslyn, + CompilerOptions.Optimize | CompilerOptions.UseRoslyn + }; + [Test] public void Comparisons([ValueSource("defaultOptions")] CompilerOptions options) { @@ -168,6 +174,12 @@ namespace ICSharpCode.Decompiler.Tests RunCS(options: options); } + [Test] + public void NullPropagation([ValueSource("roslynOnlyOptions")] CompilerOptions options) + { + RunCS(options: options); + } + [Test] public void BitNot([Values(false, true)] bool force32Bit) { @@ -255,7 +267,7 @@ namespace ICSharpCode.Decompiler.Tests try { outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options, outputFileName: Path.Combine(TestCasePath, testOutputFileName)); - string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, GetSettings(options)); + string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options)); decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options); Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile); @@ -277,7 +289,7 @@ namespace ICSharpCode.Decompiler.Tests try { outputFile = Tester.AssembleIL(Path.Combine(TestCasePath, testFileName), asmOptions); - string decompiledCodeFile = Tester.DecompileCSharp(outputFile, GetSettings(options)); + string decompiledCodeFile = Tester.DecompileCSharp(outputFile, Tester.GetSettings(options)); decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options); Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile); @@ -289,15 +301,5 @@ namespace ICSharpCode.Decompiler.Tests decompiledOutputFile.TempFiles.Delete(); } } - - DecompilerSettings GetSettings(CompilerOptions options) - { - if (!options.HasFlag(CompilerOptions.UseRoslyn)) { - return new DecompilerSettings { - StringInterpolation = false - }; - } - return new DecompilerSettings(); - } } } diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index feef9b6e3..3d06cbbad 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -238,6 +238,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers } } + internal static DecompilerSettings GetSettings(CompilerOptions cscOptions) + { + var settings = new DecompilerSettings(); + if ((cscOptions & CompilerOptions.UseRoslyn) == 0) { + // disable C# features not available in legacy compiler + settings.NullPropagation = false; + settings.StringInterpolation = false; + } + return settings; + } + public static CSharpDecompiler GetDecompilerForSnippet(string csharpText) { var syntaxTree = SyntaxFactory.ParseSyntaxTree(csharpText); diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index b845f188c..6c34c8efb 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -68,11 +68,13 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 2ee622d48..190924d9d 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -94,7 +94,9 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void ExceptionHandling([ValueSource("defaultOptions")] CompilerOptions cscOptions) { - RunForLibrary(cscOptions: cscOptions); + RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { + NullPropagation = false + }); } [Test] @@ -236,6 +238,12 @@ namespace ICSharpCode.Decompiler.Tests Run(cscOptions: cscOptions); } + [Test] + public void NullPropagation([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) { Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings); @@ -265,7 +273,7 @@ namespace ICSharpCode.Decompiler.Tests } var executable = Tester.AssembleIL(ilFile, asmOptions); - var decompiled = Tester.DecompileCSharp(executable, decompilerSettings); + var decompiled = Tester.DecompileCSharp(executable, decompilerSettings ?? Tester.GetSettings(cscOptions)); CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(cscOptions).ToArray()); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs new file mode 100644 index 000000000..62e7875ab --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs @@ -0,0 +1,29 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness +{ + class NullPropagation + { + static void Main() + { + new NullPropagation().TestNotCoalescing(); + } + + class MyClass + { + public string Text; + } + + void TestNotCoalescing() + { + Console.WriteLine("TestNotCoalescing:"); + Console.WriteLine(NotCoalescing(null)); + Console.WriteLine(NotCoalescing(new MyClass())); + } + + string NotCoalescing(MyClass c) + { + return c != null ? c.Text : "Hello"; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs new file mode 100644 index 000000000..17606500d --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs @@ -0,0 +1,213 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class NullPropagation + { + private class MyClass + { + public int IntVal; + public string Text; + public MyClass Field; + public MyClass Property { + get; + set; + } + public MyClass this[int index] { + get { + return null; + } + } + public MyClass Method(int arg) + { + return null; + } + + public void Done() + { + } + } + + private struct MyStruct + { + public int IntVal; + public MyClass Field; + public MyStruct? Property1 { + get { + return null; + } + } + public MyStruct Property2 { + get { + return default(MyStruct); + } + } + public MyStruct? this[int index] { + get { + return null; + } + } + public MyStruct? Method1(int arg) + { + return null; + } + public MyStruct Method2(int arg) + { + return default(MyStruct); + } + + public void Done() + { + } + } + + private int GetInt() + { + return 9; + } + + private string GetString() + { + return null; + } + + private MyClass GetMyClass() + { + return null; + } + + private MyStruct? GetMyStruct() + { + return null; + } + + public string Substring() + { + return this.GetString()?.Substring(this.GetInt()); + } + + public void CallSubstringAndIgnoreResult() + { + this.GetString()?.Substring(this.GetInt()); + } + + private void Use(T t) + { + } + + public void CallDone() + { + this.GetMyClass()?.Done(); + this.GetMyClass()?.Field?.Done(); + this.GetMyClass()?.Field.Done(); + this.GetMyClass()?.Property?.Done(); + this.GetMyClass()?.Property.Done(); + this.GetMyClass()?.Method(this.GetInt())?.Done(); + this.GetMyClass()?.Method(this.GetInt()).Done(); + this.GetMyClass()?[this.GetInt()]?.Done(); + this.GetMyClass()?[this.GetInt()].Done(); + } + + public void CallDoneStruct() + { + this.GetMyStruct()?.Done(); +#if STRUCT_SPLITTING_IMPROVED + this.GetMyStruct()?.Field?.Done(); + this.GetMyStruct()?.Field.Done(); + this.GetMyStruct()?.Property1?.Done(); + this.GetMyStruct()?.Property2.Done(); + this.GetMyStruct()?.Method1(this.GetInt())?.Done(); + this.GetMyStruct()?.Method2(this.GetInt()).Done(); + this.GetMyStruct()?[this.GetInt()]?.Done(); +#endif + } + + public void RequiredParentheses() + { + (this.GetMyClass()?.Field).Done(); + (this.GetMyClass()?.Method(this.GetInt())).Done(); +#if STRUCT_SPLITTING_IMPROVED + (GetMyStruct()?.Property2)?.Done(); +#endif + } + + public int?[] ChainsOnClass() + { + return new int?[9] { + this.GetMyClass()?.IntVal, + this.GetMyClass()?.Field.IntVal, + this.GetMyClass()?.Field?.IntVal, + this.GetMyClass()?.Property.IntVal, + this.GetMyClass()?.Property?.IntVal, + this.GetMyClass()?.Method(this.GetInt()).IntVal, + this.GetMyClass()?.Method(this.GetInt())?.IntVal, + this.GetMyClass()?[this.GetInt()].IntVal, + this.GetMyClass()?[this.GetInt()]?.IntVal + }; + } + +#if STRUCT_SPLITTING_IMPROVED + public int?[] ChainsStruct() + { + return new int?[8] { + this.GetMyStruct()?.IntVal, + this.GetMyStruct()?.Field.IntVal, + this.GetMyStruct()?.Field?.IntVal, + this.GetMyStruct()?.Property2.IntVal, + this.GetMyStruct()?.Property1?.IntVal, + this.GetMyStruct()?.Method2(this.GetInt()).IntVal, + this.GetMyStruct()?.Method1(this.GetInt())?.IntVal, + this.GetMyStruct()?[this.GetInt()]?.IntVal + }; + } +#endif + + public int CoalescingReturn() + { + return this.GetMyClass()?.IntVal ?? 1; + } + + public void Coalescing() + { + this.Use(this.GetMyClass()?.IntVal ?? 1); + } + + public void CoalescingString() + { + this.Use(this.GetMyClass()?.Text ?? "Hello"); + } + + public void InvokeDelegate(EventHandler eh) + { + eh?.Invoke(null, EventArgs.Empty); + } + + public int? InvokeDelegate(Func f) + { + return f?.Invoke(); + } + + private void NotNullPropagation(MyClass c) + { + // don't decompile this to "(c?.IntVal ?? 0) != 0" + if (c != null && c.IntVal != 0) { + Console.WriteLine("non-zero"); + } + if (c == null || c.IntVal == 0) { + Console.WriteLine("null or zero"); + } + Console.WriteLine("end of method"); + } + + private void Setter(MyClass c) + { + if (c != null) { + c.IntVal = 1; + } + Console.WriteLine(); + if (c != null) { + c.Property = null; + } + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.opt.roslyn.il new file mode 100644 index 000000000..38bb4cd34 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.opt.roslyn.il @@ -0,0 +1,863 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly NullPropagation +{ + .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 + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module NullPropagation.dll +// MVID: {DDAB2C82-C901-450A-B55D-2D0D7BC34C48} +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x003E0000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation + extends [mscorlib]System.Object +{ + .class auto ansi nested private beforefieldinit MyClass + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6D 00 00 ) // ...Item.. + .field public int32 IntVal + .field public string Text + .field public class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass Field + .field private class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig specialname + instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + get_Property() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::'k__BackingField' + IL_0006: ret + } // end of method MyClass::get_Property + + .method public hidebysig specialname + instance void set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::'k__BackingField' + IL_0007: ret + } // end of method MyClass::set_Property + + .method public hidebysig specialname + instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + get_Item(int32 index) cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: ret + } // end of method MyClass::get_Item + + .method public hidebysig instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + Method(int32 arg) cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: ret + } // end of method MyClass::Method + + .method public hidebysig instance void + Done() cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method MyClass::Done + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method MyClass::.ctor + + .property instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + Property() + { + .get instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass) + } // end of property MyClass::Property + .property instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + Item(int32) + { + .get instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + } // end of property MyClass::Item + } // end of class MyClass + + .class sequential ansi sealed nested private beforefieldinit MyStruct + extends [mscorlib]System.ValueType + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6D 00 00 ) // ...Item.. + .field public int32 IntVal + .field public class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass Field + .method public hidebysig specialname + instance valuetype [mscorlib]System.Nullable`1 + get_Property1() cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj valuetype [mscorlib]System.Nullable`1 + IL_0008: ldloc.0 + IL_0009: ret + } // end of method MyStruct::get_Property1 + + .method public hidebysig specialname + instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + get_Property2() cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + IL_0008: ldloc.0 + IL_0009: ret + } // end of method MyStruct::get_Property2 + + .method public hidebysig specialname + instance valuetype [mscorlib]System.Nullable`1 + get_Item(int32 index) cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj valuetype [mscorlib]System.Nullable`1 + IL_0008: ldloc.0 + IL_0009: ret + } // end of method MyStruct::get_Item + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + Method1(int32 arg) cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj valuetype [mscorlib]System.Nullable`1 + IL_0008: ldloc.0 + IL_0009: ret + } // end of method MyStruct::Method1 + + .method public hidebysig instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + Method2(int32 arg) cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + IL_0008: ldloc.0 + IL_0009: ret + } // end of method MyStruct::Method2 + + .method public hidebysig instance void + Done() cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method MyStruct::Done + + .property instance valuetype [mscorlib]System.Nullable`1 + Property1() + { + .get instance valuetype [mscorlib]System.Nullable`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Property1() + } // end of property MyStruct::Property1 + .property instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + Property2() + { + .get instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Property2() + } // end of property MyStruct::Property2 + .property instance valuetype [mscorlib]System.Nullable`1 + Item(int32) + { + .get instance valuetype [mscorlib]System.Nullable`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Item(int32) + } // end of property MyStruct::Item + } // end of class MyStruct + + .method private hidebysig instance int32 + GetInt() cil managed + { + // Code size 3 (0x3) + .maxstack 8 + IL_0000: ldc.i4.s 9 + IL_0002: ret + } // end of method NullPropagation::GetInt + + .method private hidebysig instance string + GetString() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: ret + } // end of method NullPropagation::GetString + + .method private hidebysig instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + GetMyClass() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: ret + } // end of method NullPropagation::GetMyClass + + .method private hidebysig instance valuetype [mscorlib]System.Nullable`1 + GetMyStruct() cil managed + { + // Code size 10 (0xa) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj valuetype [mscorlib]System.Nullable`1 + IL_0008: ldloc.0 + IL_0009: ret + } // end of method NullPropagation::GetMyStruct + + .method public hidebysig instance string + Substring() cil managed + { + // Code size 24 (0x18) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetString() + IL_0006: dup + IL_0007: brtrue.s IL_000c + + IL_0009: pop + IL_000a: ldnull + IL_000b: ret + + IL_000c: ldarg.0 + IL_000d: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0012: call instance string [mscorlib]System.String::Substring(int32) + IL_0017: ret + } // end of method NullPropagation::Substring + + .method public hidebysig instance void + CallSubstringAndIgnoreResult() cil managed + { + // Code size 24 (0x18) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetString() + IL_0006: dup + IL_0007: brtrue.s IL_000b + + IL_0009: pop + IL_000a: ret + + IL_000b: ldarg.0 + IL_000c: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0011: call instance string [mscorlib]System.String::Substring(int32) + IL_0016: pop + IL_0017: ret + } // end of method NullPropagation::CallSubstringAndIgnoreResult + + .method private hidebysig instance void + Use(!!T t) cil managed + { + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method NullPropagation::Use + + .method public hidebysig instance void + CallDone() cil managed + { + // Code size 241 (0xf1) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0006: dup + IL_0007: brtrue.s IL_000c + + IL_0009: pop + IL_000a: br.s IL_0011 + + IL_000c: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0011: ldarg.0 + IL_0012: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0017: dup + IL_0018: brtrue.s IL_001d + + IL_001a: pop + IL_001b: br.s IL_002d + + IL_001d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0022: dup + IL_0023: brtrue.s IL_0028 + + IL_0025: pop + IL_0026: br.s IL_002d + + IL_0028: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_002d: ldarg.0 + IL_002e: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0033: dup + IL_0034: brtrue.s IL_0039 + + IL_0036: pop + IL_0037: br.s IL_0043 + + IL_0039: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_003e: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0043: ldarg.0 + IL_0044: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0049: dup + IL_004a: brtrue.s IL_004f + + IL_004c: pop + IL_004d: br.s IL_005f + + IL_004f: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_0054: dup + IL_0055: brtrue.s IL_005a + + IL_0057: pop + IL_0058: br.s IL_005f + + IL_005a: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_005f: ldarg.0 + IL_0060: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0065: dup + IL_0066: brtrue.s IL_006b + + IL_0068: pop + IL_0069: br.s IL_0075 + + IL_006b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_0070: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0075: ldarg.0 + IL_0076: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_007b: dup + IL_007c: brtrue.s IL_0081 + + IL_007e: pop + IL_007f: br.s IL_0097 + + IL_0081: ldarg.0 + IL_0082: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0087: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_008c: dup + IL_008d: brtrue.s IL_0092 + + IL_008f: pop + IL_0090: br.s IL_0097 + + IL_0092: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0097: ldarg.0 + IL_0098: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_009d: dup + IL_009e: brtrue.s IL_00a3 + + IL_00a0: pop + IL_00a1: br.s IL_00b3 + + IL_00a3: ldarg.0 + IL_00a4: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_00a9: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_00ae: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_00b3: ldarg.0 + IL_00b4: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00b9: dup + IL_00ba: brtrue.s IL_00bf + + IL_00bc: pop + IL_00bd: br.s IL_00d5 + + IL_00bf: ldarg.0 + IL_00c0: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_00c5: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_00ca: dup + IL_00cb: brtrue.s IL_00d0 + + IL_00cd: pop + IL_00ce: br.s IL_00d5 + + IL_00d0: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_00d5: ldarg.0 + IL_00d6: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00db: dup + IL_00dc: brtrue.s IL_00e0 + + IL_00de: pop + IL_00df: ret + + IL_00e0: ldarg.0 + IL_00e1: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_00e6: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_00eb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_00f0: ret + } // end of method NullPropagation::CallDone + + .method public hidebysig instance void + CallDoneStruct() cil managed + { + // Code size 33 (0x21) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_1) + IL_0000: ldarg.0 + IL_0001: call instance valuetype [mscorlib]System.Nullable`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyStruct() + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: dup + IL_000a: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_000f: brtrue.s IL_0013 + + IL_0011: pop + IL_0012: ret + + IL_0013: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::Done() + IL_0020: ret + } // end of method NullPropagation::CallDoneStruct + + .method public hidebysig instance void + RequiredParentheses() cil managed + { + // Code size 53 (0x35) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0006: dup + IL_0007: brtrue.s IL_000d + + IL_0009: pop + IL_000a: ldnull + IL_000b: br.s IL_0012 + + IL_000d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0012: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0017: ldarg.0 + IL_0018: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_001d: dup + IL_001e: brtrue.s IL_0024 + + IL_0020: pop + IL_0021: ldnull + IL_0022: br.s IL_002f + + IL_0024: ldarg.0 + IL_0025: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_002a: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_002f: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0034: ret + } // end of method NullPropagation::RequiredParentheses + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1[] + ChainsOnClass() cil managed + { + // Code size 474 (0x1da) + .maxstack 5 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: ldc.i4.s 9 + IL_0002: newarr valuetype [mscorlib]System.Nullable`1 + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldarg.0 + IL_000a: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_000f: dup + IL_0010: brtrue.s IL_001e + + IL_0012: pop + IL_0013: ldloca.s V_0 + IL_0015: initobj valuetype [mscorlib]System.Nullable`1 + IL_001b: ldloc.0 + IL_001c: br.s IL_0028 + + IL_001e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0023: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0028: stelem valuetype [mscorlib]System.Nullable`1 + IL_002d: dup + IL_002e: ldc.i4.1 + IL_002f: ldarg.0 + IL_0030: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0035: dup + IL_0036: brtrue.s IL_0044 + + IL_0038: pop + IL_0039: ldloca.s V_0 + IL_003b: initobj valuetype [mscorlib]System.Nullable`1 + IL_0041: ldloc.0 + IL_0042: br.s IL_0053 + + IL_0044: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0049: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_004e: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0053: stelem valuetype [mscorlib]System.Nullable`1 + IL_0058: dup + IL_0059: ldc.i4.2 + IL_005a: ldarg.0 + IL_005b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0060: dup + IL_0061: brtrue.s IL_006f + + IL_0063: pop + IL_0064: ldloca.s V_0 + IL_0066: initobj valuetype [mscorlib]System.Nullable`1 + IL_006c: ldloc.0 + IL_006d: br.s IL_008d + + IL_006f: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0074: dup + IL_0075: brtrue.s IL_0083 + + IL_0077: pop + IL_0078: ldloca.s V_0 + IL_007a: initobj valuetype [mscorlib]System.Nullable`1 + IL_0080: ldloc.0 + IL_0081: br.s IL_008d + + IL_0083: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0088: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_008d: stelem valuetype [mscorlib]System.Nullable`1 + IL_0092: dup + IL_0093: ldc.i4.3 + IL_0094: ldarg.0 + IL_0095: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_009a: dup + IL_009b: brtrue.s IL_00a9 + + IL_009d: pop + IL_009e: ldloca.s V_0 + IL_00a0: initobj valuetype [mscorlib]System.Nullable`1 + IL_00a6: ldloc.0 + IL_00a7: br.s IL_00b8 + + IL_00a9: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_00ae: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_00b3: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_00b8: stelem valuetype [mscorlib]System.Nullable`1 + IL_00bd: dup + IL_00be: ldc.i4.4 + IL_00bf: ldarg.0 + IL_00c0: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00c5: dup + IL_00c6: brtrue.s IL_00d4 + + IL_00c8: pop + IL_00c9: ldloca.s V_0 + IL_00cb: initobj valuetype [mscorlib]System.Nullable`1 + IL_00d1: ldloc.0 + IL_00d2: br.s IL_00f2 + + IL_00d4: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_00d9: dup + IL_00da: brtrue.s IL_00e8 + + IL_00dc: pop + IL_00dd: ldloca.s V_0 + IL_00df: initobj valuetype [mscorlib]System.Nullable`1 + IL_00e5: ldloc.0 + IL_00e6: br.s IL_00f2 + + IL_00e8: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_00ed: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_00f2: stelem valuetype [mscorlib]System.Nullable`1 + IL_00f7: dup + IL_00f8: ldc.i4.5 + IL_00f9: ldarg.0 + IL_00fa: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00ff: dup + IL_0100: brtrue.s IL_010e + + IL_0102: pop + IL_0103: ldloca.s V_0 + IL_0105: initobj valuetype [mscorlib]System.Nullable`1 + IL_010b: ldloc.0 + IL_010c: br.s IL_0123 + + IL_010e: ldarg.0 + IL_010f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0114: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_0119: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_011e: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0123: stelem valuetype [mscorlib]System.Nullable`1 + IL_0128: dup + IL_0129: ldc.i4.6 + IL_012a: ldarg.0 + IL_012b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0130: dup + IL_0131: brtrue.s IL_013f + + IL_0133: pop + IL_0134: ldloca.s V_0 + IL_0136: initobj valuetype [mscorlib]System.Nullable`1 + IL_013c: ldloc.0 + IL_013d: br.s IL_0163 + + IL_013f: ldarg.0 + IL_0140: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0145: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_014a: dup + IL_014b: brtrue.s IL_0159 + + IL_014d: pop + IL_014e: ldloca.s V_0 + IL_0150: initobj valuetype [mscorlib]System.Nullable`1 + IL_0156: ldloc.0 + IL_0157: br.s IL_0163 + + IL_0159: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_015e: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0163: stelem valuetype [mscorlib]System.Nullable`1 + IL_0168: dup + IL_0169: ldc.i4.7 + IL_016a: ldarg.0 + IL_016b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0170: dup + IL_0171: brtrue.s IL_017f + + IL_0173: pop + IL_0174: ldloca.s V_0 + IL_0176: initobj valuetype [mscorlib]System.Nullable`1 + IL_017c: ldloc.0 + IL_017d: br.s IL_0194 + + IL_017f: ldarg.0 + IL_0180: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0185: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_018a: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_018f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0194: stelem valuetype [mscorlib]System.Nullable`1 + IL_0199: dup + IL_019a: ldc.i4.8 + IL_019b: ldarg.0 + IL_019c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_01a1: dup + IL_01a2: brtrue.s IL_01b0 + + IL_01a4: pop + IL_01a5: ldloca.s V_0 + IL_01a7: initobj valuetype [mscorlib]System.Nullable`1 + IL_01ad: ldloc.0 + IL_01ae: br.s IL_01d4 + + IL_01b0: ldarg.0 + IL_01b1: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_01b6: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_01bb: dup + IL_01bc: brtrue.s IL_01ca + + IL_01be: pop + IL_01bf: ldloca.s V_0 + IL_01c1: initobj valuetype [mscorlib]System.Nullable`1 + IL_01c7: ldloc.0 + IL_01c8: br.s IL_01d4 + + IL_01ca: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_01cf: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_01d4: stelem valuetype [mscorlib]System.Nullable`1 + IL_01d9: ret + } // end of method NullPropagation::ChainsOnClass + + .method public hidebysig instance int32 + CoalescingReturn() cil managed + { + // Code size 18 (0x12) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0006: dup + IL_0007: brtrue.s IL_000c + + IL_0009: pop + IL_000a: ldc.i4.1 + IL_000b: ret + + IL_000c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0011: ret + } // end of method NullPropagation::CoalescingReturn + + .method public hidebysig instance void + Coalescing() cil managed + { + // Code size 25 (0x19) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0007: dup + IL_0008: brtrue.s IL_000e + + IL_000a: pop + IL_000b: ldc.i4.1 + IL_000c: br.s IL_0013 + + IL_000e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::Use(!!0) + IL_0018: ret + } // end of method NullPropagation::Coalescing + + .method public hidebysig instance void + CoalescingString() cil managed + { + // Code size 34 (0x22) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0007: dup + IL_0008: brtrue.s IL_000e + + IL_000a: pop + IL_000b: ldnull + IL_000c: br.s IL_0013 + + IL_000e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Text + IL_0013: dup + IL_0014: brtrue.s IL_001c + + IL_0016: pop + IL_0017: ldstr "Hello" + IL_001c: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::Use(!!0) + IL_0021: ret + } // end of method NullPropagation::CoalescingString + + .method public hidebysig instance void + InvokeDelegate(class [mscorlib]System.EventHandler eh) cil managed + { + // Code size 16 (0x10) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: brfalse.s IL_000f + + IL_0003: ldarg.1 + IL_0004: ldnull + IL_0005: ldsfld class [mscorlib]System.EventArgs [mscorlib]System.EventArgs::Empty + IL_000a: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, + class [mscorlib]System.EventArgs) + IL_000f: ret + } // end of method NullPropagation::InvokeDelegate + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + InvokeDelegate(class [mscorlib]System.Func`1 f) cil managed + { + // Code size 25 (0x19) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0) + IL_0000: ldarg.1 + IL_0001: brtrue.s IL_000d + + IL_0003: ldloca.s V_0 + IL_0005: initobj valuetype [mscorlib]System.Nullable`1 + IL_000b: ldloc.0 + IL_000c: ret + + IL_000d: ldarg.1 + IL_000e: callvirt instance !0 class [mscorlib]System.Func`1::Invoke() + IL_0013: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0018: ret + } // end of method NullPropagation::InvokeDelegate + + .method private hidebysig instance void + NotNullPropagation(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass c) cil managed + { + // Code size 53 (0x35) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: brfalse.s IL_0015 + + IL_0003: ldarg.1 + IL_0004: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0009: brfalse.s IL_0015 + + IL_000b: ldstr "non-zero" + IL_0010: call void [mscorlib]System.Console::WriteLine(string) + IL_0015: ldarg.1 + IL_0016: brfalse.s IL_0020 + + IL_0018: ldarg.1 + IL_0019: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_001e: brtrue.s IL_002a + + IL_0020: ldstr "null or zero" + IL_0025: call void [mscorlib]System.Console::WriteLine(string) + IL_002a: ldstr "end of method" + IL_002f: call void [mscorlib]System.Console::WriteLine(string) + IL_0034: ret + } // end of method NullPropagation::NotNullPropagation + + .method private hidebysig instance void + Setter(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass c) cil managed + { + // Code size 26 (0x1a) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: brfalse.s IL_000a + + IL_0003: ldarg.1 + IL_0004: ldc.i4.1 + IL_0005: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_000a: call void [mscorlib]System.Console::WriteLine() + IL_000f: ldarg.1 + IL_0010: brfalse.s IL_0019 + + IL_0012: ldarg.1 + IL_0013: ldnull + IL_0014: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass) + IL_0019: ret + } // end of method NullPropagation::Setter + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method NullPropagation::.ctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.roslyn.il new file mode 100644 index 000000000..1d717edd7 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.roslyn.il @@ -0,0 +1,1024 @@ + +// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0 +// Copyright (c) Microsoft Corporation. All rights reserved. + + + +// Metadata version: v4.0.30319 +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly NullPropagation +{ + .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 + 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. + + // --- The following custom attribute is added automatically, do not uncomment ------- + // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) + + .permissionset reqmin + = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 0:0:0:0 +} +.module NullPropagation.dll +// MVID: {252A7D2B-A310-4825-9B38-400B4B7D3296} +.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.imagebase 0x10000000 +.file alignment 0x00000200 +.stackreserve 0x00100000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000001 // ILONLY +// Image base: 0x03130000 + + +// =============== CLASS MEMBERS DECLARATION =================== + +.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation + extends [mscorlib]System.Object +{ + .class auto ansi nested private beforefieldinit MyClass + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6D 00 00 ) // ...Item.. + .field public int32 IntVal + .field public string Text + .field public class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass Field + .field private class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass 'k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) + .method public hidebysig specialname + instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + get_Property() cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::'k__BackingField' + IL_0006: ret + } // end of method MyClass::get_Property + + .method public hidebysig specialname + instance void set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass 'value') cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::'k__BackingField' + IL_0007: ret + } // end of method MyClass::set_Property + + .method public hidebysig specialname + instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + get_Item(int32 index) cil managed + { + // Code size 7 (0x7) + .maxstack 1 + .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method MyClass::get_Item + + .method public hidebysig instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + Method(int32 arg) cil managed + { + // Code size 7 (0x7) + .maxstack 1 + .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method MyClass::Method + + .method public hidebysig instance void + Done() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method MyClass::Done + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyClass::.ctor + + .property instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + Property() + { + .get instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + .set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass) + } // end of property MyClass::Property + .property instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + Item(int32) + { + .get instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + } // end of property MyClass::Item + } // end of class MyClass + + .class sequential ansi sealed nested private beforefieldinit MyStruct + extends [mscorlib]System.ValueType + { + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6D 00 00 ) // ...Item.. + .field public int32 IntVal + .field public class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass Field + .method public hidebysig specialname + instance valuetype [mscorlib]System.Nullable`1 + get_Property1() cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj valuetype [mscorlib]System.Nullable`1 + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method MyStruct::get_Property1 + + .method public hidebysig specialname + instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + get_Property2() cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method MyStruct::get_Property2 + + .method public hidebysig specialname + instance valuetype [mscorlib]System.Nullable`1 + get_Item(int32 index) cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj valuetype [mscorlib]System.Nullable`1 + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method MyStruct::get_Item + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + Method1(int32 arg) cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj valuetype [mscorlib]System.Nullable`1 + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method MyStruct::Method1 + + .method public hidebysig instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + Method2(int32 arg) cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method MyStruct::Method2 + + .method public hidebysig instance void + Done() cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method MyStruct::Done + + .property instance valuetype [mscorlib]System.Nullable`1 + Property1() + { + .get instance valuetype [mscorlib]System.Nullable`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Property1() + } // end of property MyStruct::Property1 + .property instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct + Property2() + { + .get instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Property2() + } // end of property MyStruct::Property2 + .property instance valuetype [mscorlib]System.Nullable`1 + Item(int32) + { + .get instance valuetype [mscorlib]System.Nullable`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Item(int32) + } // end of property MyStruct::Item + } // end of class MyStruct + + .method private hidebysig instance int32 + GetInt() cil managed + { + // Code size 8 (0x8) + .maxstack 1 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldc.i4.s 9 + IL_0003: stloc.0 + IL_0004: br.s IL_0006 + + IL_0006: ldloc.0 + IL_0007: ret + } // end of method NullPropagation::GetInt + + .method private hidebysig instance string + GetString() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method NullPropagation::GetString + + .method private hidebysig instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass + GetMyClass() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + .locals init (class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass V_0) + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method NullPropagation::GetMyClass + + .method private hidebysig instance valuetype [mscorlib]System.Nullable`1 + GetMyStruct() cil managed + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1) + IL_0000: nop + IL_0001: ldloca.s V_0 + IL_0003: initobj valuetype [mscorlib]System.Nullable`1 + IL_0009: ldloc.0 + IL_000a: stloc.1 + IL_000b: br.s IL_000d + + IL_000d: ldloc.1 + IL_000e: ret + } // end of method NullPropagation::GetMyStruct + + .method public hidebysig instance string + Substring() cil managed + { + // Code size 30 (0x1e) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetString() + IL_0007: dup + IL_0008: brtrue.s IL_000e + + IL_000a: pop + IL_000b: ldnull + IL_000c: br.s IL_0019 + + IL_000e: ldarg.0 + IL_000f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0014: call instance string [mscorlib]System.String::Substring(int32) + IL_0019: stloc.0 + IL_001a: br.s IL_001c + + IL_001c: ldloc.0 + IL_001d: ret + } // end of method NullPropagation::Substring + + .method public hidebysig instance void + CallSubstringAndIgnoreResult() cil managed + { + // Code size 26 (0x1a) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetString() + IL_0007: dup + IL_0008: brtrue.s IL_000d + + IL_000a: pop + IL_000b: br.s IL_0019 + + IL_000d: ldarg.0 + IL_000e: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0013: call instance string [mscorlib]System.String::Substring(int32) + IL_0018: pop + IL_0019: ret + } // end of method NullPropagation::CallSubstringAndIgnoreResult + + .method private hidebysig instance void + Use(!!T t) cil managed + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: nop + IL_0001: ret + } // end of method NullPropagation::Use + + .method public hidebysig instance void + CallDone() cil managed + { + // Code size 252 (0xfc) + .maxstack 2 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0007: dup + IL_0008: brtrue.s IL_000d + + IL_000a: pop + IL_000b: br.s IL_0013 + + IL_000d: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0012: nop + IL_0013: ldarg.0 + IL_0014: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0019: dup + IL_001a: brtrue.s IL_001f + + IL_001c: pop + IL_001d: br.s IL_0030 + + IL_001f: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0024: dup + IL_0025: brtrue.s IL_002a + + IL_0027: pop + IL_0028: br.s IL_0030 + + IL_002a: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_002f: nop + IL_0030: ldarg.0 + IL_0031: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0036: dup + IL_0037: brtrue.s IL_003c + + IL_0039: pop + IL_003a: br.s IL_0047 + + IL_003c: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0041: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0046: nop + IL_0047: ldarg.0 + IL_0048: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_004d: dup + IL_004e: brtrue.s IL_0053 + + IL_0050: pop + IL_0051: br.s IL_0064 + + IL_0053: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_0058: dup + IL_0059: brtrue.s IL_005e + + IL_005b: pop + IL_005c: br.s IL_0064 + + IL_005e: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0063: nop + IL_0064: ldarg.0 + IL_0065: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_006a: dup + IL_006b: brtrue.s IL_0070 + + IL_006d: pop + IL_006e: br.s IL_007b + + IL_0070: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_0075: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_007a: nop + IL_007b: ldarg.0 + IL_007c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0081: dup + IL_0082: brtrue.s IL_0087 + + IL_0084: pop + IL_0085: br.s IL_009e + + IL_0087: ldarg.0 + IL_0088: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_008d: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_0092: dup + IL_0093: brtrue.s IL_0098 + + IL_0095: pop + IL_0096: br.s IL_009e + + IL_0098: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_009d: nop + IL_009e: ldarg.0 + IL_009f: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00a4: dup + IL_00a5: brtrue.s IL_00aa + + IL_00a7: pop + IL_00a8: br.s IL_00bb + + IL_00aa: ldarg.0 + IL_00ab: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_00b0: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_00b5: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_00ba: nop + IL_00bb: ldarg.0 + IL_00bc: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00c1: dup + IL_00c2: brtrue.s IL_00c7 + + IL_00c4: pop + IL_00c5: br.s IL_00de + + IL_00c7: ldarg.0 + IL_00c8: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_00cd: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_00d2: dup + IL_00d3: brtrue.s IL_00d8 + + IL_00d5: pop + IL_00d6: br.s IL_00de + + IL_00d8: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_00dd: nop + IL_00de: ldarg.0 + IL_00df: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00e4: dup + IL_00e5: brtrue.s IL_00ea + + IL_00e7: pop + IL_00e8: br.s IL_00fb + + IL_00ea: ldarg.0 + IL_00eb: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_00f0: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_00f5: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_00fa: nop + IL_00fb: ret + } // end of method NullPropagation::CallDone + + .method public hidebysig instance void + CallDoneStruct() cil managed + { + // Code size 36 (0x24) + .maxstack 2 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance valuetype [mscorlib]System.Nullable`1 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyStruct() + IL_0007: stloc.0 + IL_0008: ldloca.s V_0 + IL_000a: dup + IL_000b: call instance bool valuetype [mscorlib]System.Nullable`1::get_HasValue() + IL_0010: brtrue.s IL_0015 + + IL_0012: pop + IL_0013: br.s IL_0023 + + IL_0015: call instance !0 valuetype [mscorlib]System.Nullable`1::GetValueOrDefault() + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::Done() + IL_0022: nop + IL_0023: ret + } // end of method NullPropagation::CallDoneStruct + + .method public hidebysig instance void + RequiredParentheses() cil managed + { + // Code size 56 (0x38) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0007: dup + IL_0008: brtrue.s IL_000e + + IL_000a: pop + IL_000b: ldnull + IL_000c: br.s IL_0013 + + IL_000e: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0013: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0018: nop + IL_0019: ldarg.0 + IL_001a: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_001f: dup + IL_0020: brtrue.s IL_0026 + + IL_0022: pop + IL_0023: ldnull + IL_0024: br.s IL_0031 + + IL_0026: ldarg.0 + IL_0027: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_002c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_0031: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done() + IL_0036: nop + IL_0037: ret + } // end of method NullPropagation::RequiredParentheses + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1[] + ChainsOnClass() cil managed + { + // Code size 479 (0x1df) + .maxstack 5 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1[] V_1) + IL_0000: nop + IL_0001: ldc.i4.s 9 + IL_0003: newarr valuetype [mscorlib]System.Nullable`1 + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldarg.0 + IL_000b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0010: dup + IL_0011: brtrue.s IL_001f + + IL_0013: pop + IL_0014: ldloca.s V_0 + IL_0016: initobj valuetype [mscorlib]System.Nullable`1 + IL_001c: ldloc.0 + IL_001d: br.s IL_0029 + + IL_001f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0024: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0029: stelem valuetype [mscorlib]System.Nullable`1 + IL_002e: dup + IL_002f: ldc.i4.1 + IL_0030: ldarg.0 + IL_0031: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0036: dup + IL_0037: brtrue.s IL_0045 + + IL_0039: pop + IL_003a: ldloca.s V_0 + IL_003c: initobj valuetype [mscorlib]System.Nullable`1 + IL_0042: ldloc.0 + IL_0043: br.s IL_0054 + + IL_0045: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_004a: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_004f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0054: stelem valuetype [mscorlib]System.Nullable`1 + IL_0059: dup + IL_005a: ldc.i4.2 + IL_005b: ldarg.0 + IL_005c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0061: dup + IL_0062: brtrue.s IL_0070 + + IL_0064: pop + IL_0065: ldloca.s V_0 + IL_0067: initobj valuetype [mscorlib]System.Nullable`1 + IL_006d: ldloc.0 + IL_006e: br.s IL_008e + + IL_0070: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field + IL_0075: dup + IL_0076: brtrue.s IL_0084 + + IL_0078: pop + IL_0079: ldloca.s V_0 + IL_007b: initobj valuetype [mscorlib]System.Nullable`1 + IL_0081: ldloc.0 + IL_0082: br.s IL_008e + + IL_0084: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0089: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_008e: stelem valuetype [mscorlib]System.Nullable`1 + IL_0093: dup + IL_0094: ldc.i4.3 + IL_0095: ldarg.0 + IL_0096: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_009b: dup + IL_009c: brtrue.s IL_00aa + + IL_009e: pop + IL_009f: ldloca.s V_0 + IL_00a1: initobj valuetype [mscorlib]System.Nullable`1 + IL_00a7: ldloc.0 + IL_00a8: br.s IL_00b9 + + IL_00aa: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_00af: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_00b4: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_00b9: stelem valuetype [mscorlib]System.Nullable`1 + IL_00be: dup + IL_00bf: ldc.i4.4 + IL_00c0: ldarg.0 + IL_00c1: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_00c6: dup + IL_00c7: brtrue.s IL_00d5 + + IL_00c9: pop + IL_00ca: ldloca.s V_0 + IL_00cc: initobj valuetype [mscorlib]System.Nullable`1 + IL_00d2: ldloc.0 + IL_00d3: br.s IL_00f3 + + IL_00d5: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property() + IL_00da: dup + IL_00db: brtrue.s IL_00e9 + + IL_00dd: pop + IL_00de: ldloca.s V_0 + IL_00e0: initobj valuetype [mscorlib]System.Nullable`1 + IL_00e6: ldloc.0 + IL_00e7: br.s IL_00f3 + + IL_00e9: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_00ee: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_00f3: stelem valuetype [mscorlib]System.Nullable`1 + IL_00f8: dup + IL_00f9: ldc.i4.5 + IL_00fa: ldarg.0 + IL_00fb: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0100: dup + IL_0101: brtrue.s IL_010f + + IL_0103: pop + IL_0104: ldloca.s V_0 + IL_0106: initobj valuetype [mscorlib]System.Nullable`1 + IL_010c: ldloc.0 + IL_010d: br.s IL_0124 + + IL_010f: ldarg.0 + IL_0110: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0115: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_011a: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_011f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0124: stelem valuetype [mscorlib]System.Nullable`1 + IL_0129: dup + IL_012a: ldc.i4.6 + IL_012b: ldarg.0 + IL_012c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0131: dup + IL_0132: brtrue.s IL_0140 + + IL_0134: pop + IL_0135: ldloca.s V_0 + IL_0137: initobj valuetype [mscorlib]System.Nullable`1 + IL_013d: ldloc.0 + IL_013e: br.s IL_0164 + + IL_0140: ldarg.0 + IL_0141: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0146: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32) + IL_014b: dup + IL_014c: brtrue.s IL_015a + + IL_014e: pop + IL_014f: ldloca.s V_0 + IL_0151: initobj valuetype [mscorlib]System.Nullable`1 + IL_0157: ldloc.0 + IL_0158: br.s IL_0164 + + IL_015a: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_015f: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0164: stelem valuetype [mscorlib]System.Nullable`1 + IL_0169: dup + IL_016a: ldc.i4.7 + IL_016b: ldarg.0 + IL_016c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0171: dup + IL_0172: brtrue.s IL_0180 + + IL_0174: pop + IL_0175: ldloca.s V_0 + IL_0177: initobj valuetype [mscorlib]System.Nullable`1 + IL_017d: ldloc.0 + IL_017e: br.s IL_0195 + + IL_0180: ldarg.0 + IL_0181: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_0186: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_018b: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0190: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_0195: stelem valuetype [mscorlib]System.Nullable`1 + IL_019a: dup + IL_019b: ldc.i4.8 + IL_019c: ldarg.0 + IL_019d: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_01a2: dup + IL_01a3: brtrue.s IL_01b1 + + IL_01a5: pop + IL_01a6: ldloca.s V_0 + IL_01a8: initobj valuetype [mscorlib]System.Nullable`1 + IL_01ae: ldloc.0 + IL_01af: br.s IL_01d5 + + IL_01b1: ldarg.0 + IL_01b2: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt() + IL_01b7: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32) + IL_01bc: dup + IL_01bd: brtrue.s IL_01cb + + IL_01bf: pop + IL_01c0: ldloca.s V_0 + IL_01c2: initobj valuetype [mscorlib]System.Nullable`1 + IL_01c8: ldloc.0 + IL_01c9: br.s IL_01d5 + + IL_01cb: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_01d0: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_01d5: stelem valuetype [mscorlib]System.Nullable`1 + IL_01da: stloc.1 + IL_01db: br.s IL_01dd + + IL_01dd: ldloc.1 + IL_01de: ret + } // end of method NullPropagation::ChainsOnClass + + .method public hidebysig instance int32 + CoalescingReturn() cil managed + { + // Code size 24 (0x18) + .maxstack 2 + .locals init (int32 V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0007: dup + IL_0008: brtrue.s IL_000e + + IL_000a: pop + IL_000b: ldc.i4.1 + IL_000c: br.s IL_0013 + + IL_000e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0013: stloc.0 + IL_0014: br.s IL_0016 + + IL_0016: ldloc.0 + IL_0017: ret + } // end of method NullPropagation::CoalescingReturn + + .method public hidebysig instance void + Coalescing() cil managed + { + // Code size 27 (0x1b) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.0 + IL_0003: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0008: dup + IL_0009: brtrue.s IL_000f + + IL_000b: pop + IL_000c: ldc.i4.1 + IL_000d: br.s IL_0014 + + IL_000f: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0014: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::Use(!!0) + IL_0019: nop + IL_001a: ret + } // end of method NullPropagation::Coalescing + + .method public hidebysig instance void + CoalescingString() cil managed + { + // Code size 36 (0x24) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.0 + IL_0003: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass() + IL_0008: dup + IL_0009: brtrue.s IL_000f + + IL_000b: pop + IL_000c: ldnull + IL_000d: br.s IL_0014 + + IL_000f: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Text + IL_0014: dup + IL_0015: brtrue.s IL_001d + + IL_0017: pop + IL_0018: ldstr "Hello" + IL_001d: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::Use(!!0) + IL_0022: nop + IL_0023: ret + } // end of method NullPropagation::CoalescingString + + .method public hidebysig instance void + InvokeDelegate(class [mscorlib]System.EventHandler eh) cil managed + { + // Code size 20 (0x14) + .maxstack 8 + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: brtrue.s IL_0006 + + IL_0004: br.s IL_0013 + + IL_0006: ldarg.1 + IL_0007: ldnull + IL_0008: ldsfld class [mscorlib]System.EventArgs [mscorlib]System.EventArgs::Empty + IL_000d: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, + class [mscorlib]System.EventArgs) + IL_0012: nop + IL_0013: ret + } // end of method NullPropagation::InvokeDelegate + + .method public hidebysig instance valuetype [mscorlib]System.Nullable`1 + InvokeDelegate(class [mscorlib]System.Func`1 f) cil managed + { + // Code size 31 (0x1f) + .maxstack 1 + .locals init (valuetype [mscorlib]System.Nullable`1 V_0, + valuetype [mscorlib]System.Nullable`1 V_1) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: brtrue.s IL_000f + + IL_0004: ldloca.s V_0 + IL_0006: initobj valuetype [mscorlib]System.Nullable`1 + IL_000c: ldloc.0 + IL_000d: br.s IL_001a + + IL_000f: ldarg.1 + IL_0010: callvirt instance !0 class [mscorlib]System.Func`1::Invoke() + IL_0015: newobj instance void valuetype [mscorlib]System.Nullable`1::.ctor(!0) + IL_001a: stloc.1 + IL_001b: br.s IL_001d + + IL_001d: ldloc.1 + IL_001e: ret + } // end of method NullPropagation::InvokeDelegate + + .method private hidebysig instance void + NotNullPropagation(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass c) cil managed + { + // Code size 77 (0x4d) + .maxstack 2 + .locals init (bool V_0, + bool V_1) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: brfalse.s IL_000f + + IL_0004: ldarg.1 + IL_0005: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_000a: ldc.i4.0 + IL_000b: cgt.un + IL_000d: br.s IL_0010 + + IL_000f: ldc.i4.0 + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: brfalse.s IL_0021 + + IL_0014: nop + IL_0015: ldstr "non-zero" + IL_001a: call void [mscorlib]System.Console::WriteLine(string) + IL_001f: nop + IL_0020: nop + IL_0021: ldarg.1 + IL_0022: brfalse.s IL_002f + + IL_0024: ldarg.1 + IL_0025: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_002a: ldc.i4.0 + IL_002b: ceq + IL_002d: br.s IL_0030 + + IL_002f: ldc.i4.1 + IL_0030: stloc.1 + IL_0031: ldloc.1 + IL_0032: brfalse.s IL_0041 + + IL_0034: nop + IL_0035: ldstr "null or zero" + IL_003a: call void [mscorlib]System.Console::WriteLine(string) + IL_003f: nop + IL_0040: nop + IL_0041: ldstr "end of method" + IL_0046: call void [mscorlib]System.Console::WriteLine(string) + IL_004b: nop + IL_004c: ret + } // end of method NullPropagation::NotNullPropagation + + .method private hidebysig instance void + Setter(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass c) cil managed + { + // Code size 43 (0x2b) + .maxstack 2 + .locals init (bool V_0, + bool V_1) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: ldnull + IL_0003: cgt.un + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: brfalse.s IL_0012 + + IL_0009: nop + IL_000a: ldarg.1 + IL_000b: ldc.i4.1 + IL_000c: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal + IL_0011: nop + IL_0012: call void [mscorlib]System.Console::WriteLine() + IL_0017: nop + IL_0018: ldarg.1 + IL_0019: ldnull + IL_001a: cgt.un + IL_001c: stloc.1 + IL_001d: ldloc.1 + IL_001e: brfalse.s IL_002a + + IL_0020: nop + IL_0021: ldarg.1 + IL_0022: ldnull + IL_0023: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass) + IL_0028: nop + IL_0029: nop + IL_002a: ret + } // end of method NullPropagation::Setter + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method NullPropagation::.ctor + +} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation + + +// ============================================================= + +// *********** DISASSEMBLY COMPLETE *********************** diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 85327455d..94355e7e2 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -127,6 +127,7 @@ namespace ICSharpCode.Decompiler.CSharp new TransformAssignment(), // inline and compound assignments new NullCoalescingTransform(), new NullableLiftingStatementTransform(), + new NullPropagationStatementTransform(), new TransformArrayInitializers(), new TransformCollectionAndObjectInitializers(), new TransformExpressionTrees() diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 88417af5f..e9fd4f5ec 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.CSharp int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0); if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) { return HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList()); - } else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate) { + } else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) { return new InvocationExpression(target, arguments.Select(arg => arg.Expression)).WithRR(rr); } else if (IsDelegateEqualityComparison(method, arguments)) { return HandleDelegateEqualityComparison(method, arguments) @@ -252,6 +252,11 @@ namespace ICSharpCode.Decompiler.CSharp } } + static bool IsNullConditional(Expression expr) + { + return expr is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional; + } + private void ModifyReturnTypeOfLambda(LambdaExpression lambda) { var resolveResult = (DecompiledLambdaResolveResult)lambda.GetResolveResult(); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 035859733..08399a7e3 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2145,6 +2145,26 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ResolveResult(inst.GetResultMethod?.ReturnType ?? SpecialType.UnknownType)); } + protected internal override TranslatedExpression VisitNullableRewrap(NullableRewrap inst, TranslationContext context) + { + var arg = Translate(inst.Argument); + IType type = arg.Type; + if (NullableType.IsNonNullableValueType(type)) { + type = NullableType.Create(compilation, type); + } + return new UnaryOperatorExpression(UnaryOperatorType.NullConditionalRewrap, arg) + .WithILInstruction(inst) + .WithRR(new ResolveResult(type)); + } + + protected internal override TranslatedExpression VisitNullableUnwrap(NullableUnwrap inst, TranslationContext context) + { + var arg = Translate(inst.Argument); + return new UnaryOperatorExpression(UnaryOperatorType.NullConditional, arg) + .WithILInstruction(inst) + .WithRR(new ResolveResult(NullableType.GetUnderlyingType(arg.Type))); + } + protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context) { string message = "Error"; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index f3f08646b..f6e7cf54b 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1085,16 +1085,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType); if (opType == UnaryOperatorType.Await) { WriteKeyword(opSymbol); - } else if (!(opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement)) { + } else if (!IsPostfixOperator(opType) && opType != UnaryOperatorType.NullConditionalRewrap) { WriteToken(opSymbol); } unaryOperatorExpression.Expression.AcceptVisitor(this); - if (opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement) { + if (IsPostfixOperator(opType)) { WriteToken(opSymbol); } EndNode(unaryOperatorExpression); } + static bool IsPostfixOperator(UnaryOperatorType op) + { + return op == UnaryOperatorType.PostIncrement + || op == UnaryOperatorType.PostDecrement + || op == UnaryOperatorType.NullConditional; + } + public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { StartNode(uncheckedExpression); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 0e4068958..7d2345283 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -36,7 +36,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor /// public bool InsertParenthesesForReadability { get; set; } - const int Primary = 16; + const int Primary = 17; + const int NullableRewrap = 16; const int QueryOrLambda = 15; const int Unary = 14; const int RelationalAndTypeTesting = 10; @@ -55,12 +56,17 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // primary expressions get parenthesized. return QueryOrLambda; } - UnaryOperatorExpression uoe = expr as UnaryOperatorExpression; - if (uoe != null) { - if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement) - return Primary; - else - return Unary; + if (expr is UnaryOperatorExpression uoe) { + switch (uoe.Operator) { + case UnaryOperatorType.PostDecrement: + case UnaryOperatorType.PostIncrement: + case UnaryOperatorType.NullConditional: + return Primary; + case UnaryOperatorType.NullConditionalRewrap: + return NullableRewrap; + default: + return Unary; + } } if (expr is CastExpression) return Unary; @@ -177,7 +183,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { // Even in readability mode, don't parenthesize casts of casts. if (!(castExpression.Expression is CastExpression)) { - ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? Primary : Unary); + ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? NullableRewrap : Unary); } // There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases // "(int)-1" is fine, but "(A)-b" is not a cast. @@ -243,11 +249,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor int precedence = GetPrecedence(binaryOperatorExpression); if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) { if (InsertParenthesesForReadability) { - ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary); + ParenthesizeIfRequired(binaryOperatorExpression.Left, NullableRewrap); if (GetBinaryOperatorType(binaryOperatorExpression.Right) == BinaryOperatorType.NullCoalescing) { ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence); } else { - ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary); + ParenthesizeIfRequired(binaryOperatorExpression.Right, NullableRewrap); } } else { // ?? is right-associative @@ -286,7 +292,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { if (InsertParenthesesForReadability) { // few people know the precedence of 'is', so always put parentheses in nice-looking mode. - ParenthesizeIfRequired(isExpression.Expression, Primary); + ParenthesizeIfRequired(isExpression.Expression, NullableRewrap); } else { ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting); } @@ -297,7 +303,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { if (InsertParenthesesForReadability) { // few people know the precedence of 'as', so always put parentheses in nice-looking mode. - ParenthesizeIfRequired(asExpression.Expression, Primary); + ParenthesizeIfRequired(asExpression.Expression, NullableRewrap); } else { ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting); } @@ -313,9 +319,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // Only ((a ? b : c) ? d : e) strictly needs the additional parentheses if (InsertParenthesesForReadability) { // Precedence of ?: can be confusing; so always put parentheses in nice-looking mode. - ParenthesizeIfRequired(conditionalExpression.Condition, Primary); - ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary); - ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary); + ParenthesizeIfRequired(conditionalExpression.Condition, NullableRewrap); + ParenthesizeIfRequired(conditionalExpression.TrueExpression, NullableRewrap); + ParenthesizeIfRequired(conditionalExpression.FalseExpression, NullableRewrap); } else { ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1); ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs index 5bc91eb76..551ba610c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs @@ -43,6 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static TokenRole DereferenceRole = new TokenRole ("*"); public readonly static TokenRole AddressOfRole = new TokenRole ("&"); public readonly static TokenRole AwaitRole = new TokenRole ("await"); + public readonly static TokenRole NullConditionalRole = new TokenRole ("?"); public UnaryOperatorExpression() { @@ -63,9 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax get { return GetChildByRole (GetOperatorRole (Operator)); } } - static Expression NoUnaryExpressionError = new ErrorExpression ("No unary expression"); public Expression Expression { - get { return GetChildByRole (Roles.Expression) ?? NoUnaryExpressionError; } + get { return GetChildByRole (Roles.Expression); } set { SetChildByRole (Roles.Expression, value); } } @@ -114,6 +114,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return AddressOfRole; case UnaryOperatorType.Await: return AwaitRole; + case UnaryOperatorType.NullConditional: + return NullConditionalRole; + case UnaryOperatorType.NullConditionalRewrap: + return null; // no syntax default: throw new NotSupportedException("Invalid value for UnaryOperatorType"); } @@ -176,6 +180,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// Get address (&a) AddressOf, /// C# 5.0 await - Await + Await, + /// C# 6 null-conditional operator. + /// Occurs as target of member reference or indexer expressions + /// to indicate ?. or ?[]. + /// Corresponds to nullable.unwrap in ILAst. + /// + NullConditional, + /// + /// Wrapper around a primary expression containing a null conditional operator. + /// Corresponds to nullable.rewrap in ILAst. + /// This has no syntax in C#, but the node is used to ensure parentheses are inserted where necessary. + /// + NullConditionalRewrap, } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 355447889..d2c6462b5 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -186,6 +186,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms case UnaryOperatorType.Decrement: case UnaryOperatorType.Await: return true; + case UnaryOperatorType.NullConditionalRewrap: + return IsValidInStatementExpression(uoe.Expression); default: return false; } diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 0f10a46ed..429f9fc89 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -201,6 +201,13 @@ namespace ICSharpCode.Decompiler.CSharp if (targetType.Kind == TypeKind.Unknown) { return this; // don't attempt to insert cast to '?' } + if (Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional && targetType.IsReferenceType == true) { + // "(T)(x?).AccessChain" is invalid, but "((T)x)?.AccessChain" is valid and equivalent + return new UnaryOperatorExpression( + UnaryOperatorType.NullConditional, + UnwrapChild(uoe.Expression).ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion) + ).WithRR(new ResolveResult(targetType)).WithoutILInstruction(); + } var compilation = expressionBuilder.compilation; bool isLifted = type.IsKnownType(KnownTypeCode.NullableOfT) && targetType.IsKnownType(KnownTypeCode.NullableOfT); IType utype = isLifted ? NullableType.GetUnderlyingType(type) : type; diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 990c4b7a9..cc729a824 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -133,6 +133,21 @@ namespace ICSharpCode.Decompiler } } + bool nullPropagation = true; + + /// + /// Decompile C# 6 ?. and ?[] operators. + /// + public bool NullPropagation { + get { return nullPropagation; } + set { + if (nullPropagation != value) { + nullPropagation = value; + OnPropertyChanged(); + } + } + } + bool automaticProperties = true; /// diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index 034e33798..24075c83d 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -271,7 +271,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis /// /// Derived classes may add to this set of flags to ensure they don't forget to override an interesting method. /// - protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable; + protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable; protected sealed override void Default(ILInstruction inst) { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index f292e443e..c9c769e62 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -287,6 +287,7 @@ + @@ -294,6 +295,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/InstructionFlags.cs b/ICSharpCode.Decompiler/IL/InstructionFlags.cs index 6c0894643..1fcd6ee6d 100644 --- a/ICSharpCode.Decompiler/IL/InstructionFlags.cs +++ b/ICSharpCode.Decompiler/IL/InstructionFlags.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL /// /// The instruction may read from local variables. /// - MayReadLocals = 0x10, + MayReadLocals = 0x10, /// /// The instruction may write to local variables. /// @@ -51,17 +51,21 @@ namespace ICSharpCode.Decompiler.IL /// MayThrow = 0x100, /// - /// The instruction may exit with a branch or return. + /// The instruction may exit with a branch or leave. /// MayBranch = 0x200, /// + /// The instruction may jump to the closest containing nullable.rewrap instruction. + /// + MayUnwrapNull = 0x400, + /// /// The instruction performs unconditional control flow, so that its endpoint is unreachable. /// /// /// If EndPointUnreachable is set, either MayThrow or MayBranch should also be set /// (unless the instruction represents an infinite loop). /// - EndPointUnreachable = 0x400, + EndPointUnreachable = 0x800, /// /// The instruction contains some kind of internal control flow. /// @@ -72,6 +76,6 @@ namespace ICSharpCode.Decompiler.IL /// Note that branch instructions don't have this flag set, because their control flow is not internal /// (and they don't have any unusual argument evaluation rules). /// - ControlFlow = 0x800, + ControlFlow = 0x1000, } } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 92e43d040..126b4db66 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -102,6 +102,13 @@ namespace ICSharpCode.Decompiler.IL ThreeValuedLogicAnd, /// Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior. ThreeValuedLogicOr, + /// The input operand must be either a nullable value type or a reference type. + /// If the input is non-null, evaluates to the (unwrapped) input. + /// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction. + NullableUnwrap, + /// Serves as jump target for the nullable.unwrap instruction. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + NullableRewrap, /// Loads a constant string. LdStr, /// Loads a constant 32-bit integer. @@ -2510,6 +2517,71 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// The input operand must be either a nullable value type or a reference type. + /// If the input is non-null, evaluates to the (unwrapped) input. + /// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction. + public sealed partial class NullableUnwrap : UnaryInstruction + { + + protected override InstructionFlags ComputeFlags() + { + return base.ComputeFlags() | InstructionFlags.MayUnwrapNull; + } + public override InstructionFlags DirectFlags { + get { + return base.DirectFlags | InstructionFlags.MayUnwrapNull; + } + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitNullableUnwrap(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitNullableUnwrap(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitNullableUnwrap(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as NullableUnwrap; + return o != null && this.Argument.PerformMatch(o.Argument, ref match); + } + } +} +namespace ICSharpCode.Decompiler.IL +{ + /// Serves as jump target for the nullable.unwrap instruction. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + public sealed partial class NullableRewrap : UnaryInstruction + { + public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) + { + } + + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitNullableRewrap(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitNullableRewrap(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitNullableRewrap(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as NullableRewrap; + return o != null && this.Argument.PerformMatch(o.Argument, ref match); + } + } +} +namespace ICSharpCode.Decompiler.IL { /// Loads a constant string. public sealed partial class LdStr : SimpleInstruction @@ -5131,6 +5203,14 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitNullableUnwrap(NullableUnwrap inst) + { + Default(inst); + } + protected internal virtual void VisitNullableRewrap(NullableRewrap inst) + { + Default(inst); + } protected internal virtual void VisitLdStr(LdStr inst) { Default(inst); @@ -5441,6 +5521,14 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst) + { + return Default(inst); + } + protected internal virtual T VisitNullableRewrap(NullableRewrap inst) + { + return Default(inst); + } protected internal virtual T VisitLdStr(LdStr inst) { return Default(inst); @@ -5751,6 +5839,14 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst, C context) + { + return Default(inst, context); + } + protected internal virtual T VisitNullableRewrap(NullableRewrap inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitLdStr(LdStr inst, C context) { return Default(inst, context); @@ -5948,6 +6044,8 @@ namespace ICSharpCode.Decompiler.IL "addressof", "3vl.logic.and", "3vl.logic.or", + "nullable.unwrap", + "nullable.rewrap", "ldstr", "ldc.i4", "ldc.i8", @@ -6163,6 +6261,16 @@ namespace ICSharpCode.Decompiler.IL right = default(ILInstruction); return false; } + public bool MatchNullableRewrap(out ILInstruction argument) + { + var inst = this as NullableRewrap; + if (inst != null) { + argument = inst.Argument; + return true; + } + argument = default(ILInstruction); + return false; + } public bool MatchLdStr(out string value) { var inst = this as LdStr; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index c02784833..0362a1e91 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -160,6 +160,15 @@ CustomClassName("ThreeValuedLogicAnd"), Binary, ResultType("O")), new OpCode("3vl.logic.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.", CustomClassName("ThreeValuedLogicOr"), Binary, ResultType("O")), + new OpCode("nullable.unwrap", "The input operand must be either a nullable value type or a reference type." + Environment.NewLine + + "If the input is non-null, evaluates to the (unwrapped) input." + Environment.NewLine + + "If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.", + Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")), + new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine + + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type)." + + "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction," + + "the nullable.rewrap instruction evaluates to null.", + Unary, CustomComputeFlags), new OpCode("ldstr", "Loads a constant string.", CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")), diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index 93fe7bae3..5926dfdd0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -56,12 +56,17 @@ namespace ICSharpCode.Decompiler.IL { base.CheckInvariant(phase); Debug.Assert(condition.ResultType == StackType.I4); - Debug.Assert(trueInst.ResultType == falseInst.ResultType); + Debug.Assert(trueInst.ResultType == falseInst.ResultType + || trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable) + || falseInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); } public override StackType ResultType { get { - return trueInst.ResultType; + if (trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) + return falseInst.ResultType; + else + return trueInst.ResultType; } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs new file mode 100644 index 000000000..ef12373ea --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs @@ -0,0 +1,79 @@ +// Copyright (c) 2018 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.Diagnostics; +using System.Linq; + +namespace ICSharpCode.Decompiler.IL +{ + partial class NullableUnwrap + { + public NullableUnwrap(StackType unwrappedType, ILInstruction argument) + : base(OpCode.NullableUnwrap, argument) + { + this.ResultType = unwrappedType; + } + + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + Debug.Assert(Argument.ResultType == StackType.O, "nullable.unwrap expects nullable type as input"); + Debug.Assert(Ancestors.Any(a => a is NullableRewrap)); + } + + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + output.Write("nullable.unwrap "); + output.Write(ResultType); + output.Write('('); + Argument.WriteTo(output, options); + output.Write(')'); + } + + public override StackType ResultType { get; } + } + + partial class NullableRewrap + { + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + Debug.Assert(Argument.HasFlag(InstructionFlags.MayUnwrapNull)); + } + + public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow; + + protected override InstructionFlags ComputeFlags() + { + // Convert MayUnwrapNull flag to ControlFlow flag. + // Also, remove EndpointUnreachable flag, because the end-point is reachable through + // the implicit nullable.unwrap branch. + const InstructionFlags flagsToRemove = InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable; + return (Argument.Flags & ~flagsToRemove) | InstructionFlags.ControlFlow; + } + + public override StackType ResultType { + get { + if (Argument.ResultType == StackType.Void) + return StackType.Void; + else + return StackType.O; + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index d9a5f75ac..2649cf16b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -292,6 +292,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (parent is NullCoalescingInstruction && NullableType.IsNullable(v.Type)) { return true; // inline nullables into ?? operator } + if (parent is NullableUnwrap && NullableType.IsNullable(v.Type)) { + return true; // inline nullables into ?. operator + } // decide based on the target into which we are inlining switch (next.OpCode) { case OpCode.Leave: @@ -354,6 +357,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst) ?? false; // If FindLoadInNext() returns null, we still can't continue searching // because we can't inline over the remainder of the blockcontainer. + } else if (expr is NullableRewrap) { + // Inlining into nullable.rewrap is OK unless the expression being inlined + // contains a nullable.wrap that isn't being re-wrapped within the expression being inlined. + if (expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull)) + return false; } foreach (var child in expr.Children) { if (!child.SlotInfo.CanInlineInto) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index ab3499fdd..0eab50bd0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Transform for constructing the NullCoalescingInstruction (if.notnull(a,b), or in C#: ??) /// Note that this transform only handles the case where a,b are reference types. /// - /// The ?? operator for nullables is handled by NullableLiftingTransform. + /// The ?? operator for nullable value types is handled by NullableLiftingTransform. /// class NullCoalescingTransform : IStatementTransform { diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs new file mode 100644 index 000000000..6065f0847 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -0,0 +1,236 @@ +// Copyright (c) 2018 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.Text; +using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Transform that converts code patterns like "v != null ? v.M() : null" to "v?.M()" + /// + struct NullPropagationTransform + { + internal static bool IsProtectedIfInst(IfInstruction ifInst) + { + // We exclude logic.and to avoid turning + // "logic.and(comp(interfaces != ldnull), call get_Count(interfaces))" + // into "if ((interfaces?.Count ?? 0) != 0)". + return (ifInst.MatchLogicAnd(out _, out _) || ifInst.MatchLogicOr(out _, out _)) + && IfInstruction.IsInConditionSlot(ifInst); + } + + readonly ILTransformContext context; + + public NullPropagationTransform(ILTransformContext context) + { + this.context = context; + } + + /// + /// Check if "condition ? trueInst : falseInst" can be simplified using the null-conditional operator. + /// Returns the replacement instruction, or null if no replacement is possible. + /// + internal ILInstruction Run(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst, Interval ilRange) + { + Debug.Assert(context.Settings.NullPropagation); + Debug.Assert(!condition.MatchLogicNot(out _), "Caller should pass in positive condition"); + if (condition is Comp comp && comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) { + if (comp.LiftingKind != ComparisonLiftingKind.None) + return null; + if (comp.Kind == ComparisonKind.Equality) { + // testedVar == null ? trueInst : falseInst + return TryNullPropagation(testedVar, falseInst, trueInst, true, ilRange); + } else if (comp.Kind == ComparisonKind.Inequality) { + return TryNullPropagation(testedVar, trueInst, falseInst, true, ilRange); + } + } else if (NullableLiftingTransform.MatchHasValueCall(condition, out testedVar)) { + // testedVar.HasValue ? trueInst : falseInst + return TryNullPropagation(testedVar, trueInst, falseInst, false, ilRange); + } + return null; + } + + /// + /// testedVar != null ? nonNullInst : nullInst + /// + ILInstruction TryNullPropagation(ILVariable testedVar, ILInstruction nonNullInst, ILInstruction nullInst, + bool testedVarHasReferenceType, Interval ilRange) + { + bool removedRewrapOrNullableCtor = false; + if (NullableLiftingTransform.MatchNullableCtor(nonNullInst, out _, out var arg)) { + nonNullInst = arg; + removedRewrapOrNullableCtor = true; + } else if (nonNullInst.MatchNullableRewrap(out arg)) { + nonNullInst = arg; + removedRewrapOrNullableCtor = true; + } + if (!IsValidAccessChain(testedVar, testedVarHasReferenceType, nonNullInst, out var varLoad)) + return null; + // note: InferType will be accurate in this case because the access chain consists of calls and field accesses + IType returnType = nonNullInst.InferType(); + if (nullInst.MatchLdNull()) { + context.Step("Null propagation (reference type)", nonNullInst); + // testedVar != null ? testedVar.AccessChain : null + // => testedVar?.AccessChain + IntroduceUnwrap(testedVar, varLoad); + return new NullableRewrap(nonNullInst) { ILRange = ilRange }; + } else if (nullInst.MatchDefaultValue(out var type) && type.IsKnownType(KnownTypeCode.NullableOfT)) { + context.Step("Null propagation (value type)", nonNullInst); + // testedVar != null ? testedVar.AccessChain : default(T?) + // => testedVar?.AccessChain + IntroduceUnwrap(testedVar, varLoad); + return new NullableRewrap(nonNullInst) { ILRange = ilRange }; + } else if (!removedRewrapOrNullableCtor && NullableType.IsNonNullableValueType(returnType)) { + context.Step("Null propagation with null coalescing", nonNullInst); + // testedVar != null ? testedVar.AccessChain : nullInst + // => testedVar?.AccessChain ?? nullInst + // (only valid if AccessChain returns a non-nullable value) + IntroduceUnwrap(testedVar, varLoad); + return new NullCoalescingInstruction( + NullCoalescingKind.NullableWithValueFallback, + new NullableRewrap(nonNullInst), + nullInst + ) { + UnderlyingResultType = nullInst.ResultType, + ILRange = ilRange + }; + } + return null; + } + + /// + /// if (x != null) x.AccessChain(); + /// => x?.AccessChain(); + /// + internal void RunStatements(Block block, int pos) + { + var ifInst = block.Instructions[pos] as IfInstruction; + if (ifInst == null || !ifInst.FalseInst.MatchNop()) + return; + if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality + && comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) { + TryNullPropForVoidCall(testedVar, true, ifInst.TrueInst as Block, ifInst); + } else if (NullableLiftingTransform.MatchHasValueCall(ifInst.Condition, out testedVar)) { + TryNullPropForVoidCall(testedVar, false, ifInst.TrueInst as Block, ifInst); + } + } + + void TryNullPropForVoidCall(ILVariable testedVar, bool testedVarHasReferenceType, Block body, IfInstruction ifInst) + { + if (body == null || body.Instructions.Count != 1) + return; + var bodyInst = body.Instructions[0]; + if (bodyInst.MatchNullableRewrap(out var arg)) { + bodyInst = arg; + } + if (!IsValidAccessChain(testedVar, testedVarHasReferenceType, bodyInst, out var varLoad)) + return; + context.Step("Null-propagation (void call)", body); + // if (testedVar != null) { testedVar.AccessChain(); } + // => testedVar?.AccessChain(); + IntroduceUnwrap(testedVar, varLoad); + ifInst.ReplaceWith(new NullableRewrap( + bodyInst + ) { ILRange = ifInst.ILRange }); + } + + bool IsValidAccessChain(ILVariable testedVar, bool testedVarHasReferenceType, ILInstruction inst, out ILInstruction finalLoad) + { + finalLoad = null; + int chainLength = 0; + while (true) { + if (IsValidEndOfChain()) { + // valid end of chain + finalLoad = inst; + return chainLength >= 1; + } else if (inst.MatchLdFld(out var target, out _)) { + inst = target; + } else if (inst is CallInstruction call && call.OpCode != OpCode.NewObj) { + if (call.Arguments.Count == 0) { + return false; + } + if (call.Method.IsStatic && !call.Method.IsExtensionMethod) { + return false; // only instance or extension methods can be called with ?. syntax + } + if (call.Method.IsAccessor && !IsGetter(call.Method)) { + return false; // setter/adder/remover cannot be called with ?. syntax + } + inst = call.Arguments[0]; + if (call.Method.DeclaringType.IsReferenceType == false && inst.MatchAddressOf(out var arg)) { + inst = arg; + } + // ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain + for (int i = 1; i < call.Arguments.Count; ++i) { + if (call.Arguments[i].HasFlag(InstructionFlags.MayUnwrapNull)) { + return false; + } + } + } else if (inst is NullableUnwrap unwrap) { + inst = unwrap.Argument; + } else { + // unknown node -> invalid chain + return false; + } + chainLength++; + } + + bool IsValidEndOfChain() + { + if (testedVarHasReferenceType) { + return inst.MatchLdLoc(testedVar); + } else { + return NullableLiftingTransform.MatchGetValueOrDefault(inst, testedVar); + } + } + } + + static bool IsGetter(IMethod method) + { + return method.AccessorOwner is IProperty p && p.Getter == method; + } + + private void IntroduceUnwrap(ILVariable testedVar, ILInstruction varLoad) + { + if (NullableLiftingTransform.MatchGetValueOrDefault(varLoad, testedVar)) { + varLoad.ReplaceWith(new NullableUnwrap( + varLoad.ResultType, + new LdLoc(testedVar) { ILRange = varLoad.Children[0].ILRange } + ) { ILRange = varLoad.ILRange }); + } else { + // Wrap varLoad in nullable.unwrap: + var children = varLoad.Parent.Children; + children[varLoad.ChildIndex] = new NullableUnwrap(testedVar.StackType, varLoad); + } + } + } + + class NullPropagationStatementTransform : IStatementTransform + { + public void Run(Block block, int pos, StatementTransformContext context) + { + if (!context.Settings.NullPropagation) + return; + new NullPropagationTransform(context).RunStatements(block, pos); + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index f6ebe5587..526bc4441 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// * lifted unary and binary operators /// * lifted comparisons /// * the ?? operator with type Nullable{T} on the left-hand-side + /// * the ?. operator (via NullPropagationTransform) /// struct NullableLiftingTransform { @@ -55,8 +56,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// public bool Run(IfInstruction ifInst) { - if (!context.Settings.LiftNullables) - return false; var lifted = Lift(ifInst, ifInst.TrueInst, ifInst.FalseInst); if (lifted != null) { ifInst.ReplaceWith(lifted); @@ -67,8 +66,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms public bool RunStatements(Block block, int pos) { - if (!context.Settings.LiftNullables) - return false; /// e.g.: // if (!condition) Block { // leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) @@ -128,6 +125,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms condition = arg; ExtensionMethods.Swap(ref trueInst, ref falseInst); } + if (context.Settings.NullPropagation && !NullPropagationTransform.IsProtectedIfInst(ifInst)) { + var nullPropagated = new NullPropagationTransform(context) + .Run(condition, trueInst, falseInst, ifInst.ILRange); + if (nullPropagated != null) + return nullPropagated; + } + if (!context.Settings.LiftNullables) + return null; if (AnalyzeCondition(condition)) { // (v1 != null && ... && vn != null) ? trueInst : falseInst // => normal lifting diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index 3d6cd1639..c16bb2f0b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -171,22 +171,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms int leaveIndex = entryPoint.Instructions.Count == 2 ? 1 : 2; int checkIndex = entryPoint.Instructions.Count == 2 ? 0 : 1; int castIndex = entryPoint.Instructions.Count == 3 ? 0 : -1; + var checkInst = entryPoint.Instructions[checkIndex]; bool isReference = objVar.Type.IsReferenceType != false; if (castIndex > -1) { if (!entryPoint.Instructions[castIndex].MatchStLoc(out var tempVar, out var isinst)) return false; if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(KnownTypeCode.IDisposable)) return false; - if (tempVar.StoreCount != 1 || tempVar.LoadCount != 2) + if (!tempVar.IsSingleDefinition) return false; - objVar = tempVar; isReference = true; + if (!MatchDisposeCheck(tempVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck)) + return false; + if (tempVar.LoadCount != numObjVarLoadsInCheck) + return false; + } else { + if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out _)) + return false; } if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop()) return false; + return true; + } + + bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck) + { + numObjVarLoadsInCheck = 2; CallVirt callVirt; if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) { - if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst)) + if (!checkInst.MatchIfInstruction(out var condition, out var disposeInst)) return false; if (!NullableLiftingTransform.MatchHasValueCall(condition, objVar)) return false; @@ -214,9 +227,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } else { ILInstruction target; - if (isReference) { + if (isReference && checkInst is NullableRewrap rewrap) { + // the null check of reference types might have been transformed into "objVar?.Dispose();" + if (!(rewrap.Argument is CallVirt cv)) + return false; + if (!(cv.Arguments.FirstOrDefault() is NullableUnwrap unwrap)) + return false; + numObjVarLoadsInCheck = 1; + callVirt = cv; + target = unwrap.Argument; + } else if (isReference) { // reference types have a null check. - if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst)) + if (!checkInst.MatchIfInstruction(out var condition, out var disposeInst)) return false; if (!condition.MatchCompNotEquals(out var left, out var right) || !left.MatchLdLoc(objVar) || !right.MatchLdNull()) return false; @@ -231,7 +253,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms target = newTarget; callVirt = cv; } else { - if (!(entryPoint.Instructions[checkIndex] is CallVirt cv)) + if (!(checkInst is CallVirt cv)) return false; target = cv.Arguments.FirstOrDefault(); if (target == null) diff --git a/doc/copyright.txt b/doc/copyright.txt index 57fe86257..a39d8148a 100644 --- a/doc/copyright.txt +++ b/doc/copyright.txt @@ -1,4 +1,4 @@ -Copyright 2011-2014 for the SharpDevelop team +Copyright 2011-2018 for the SharpDevelop team by AlphaSierraPapa, Christoph Wille